M .build.yml => .build.yml +11 -5
@@ 1,16 1,22 @@
image: nixos/unstable
+
packages:
- nixos.cachix
+
environment:
NIX_CONFIG: "experimental-features = nix-command flakes"
+ repo: "sistemer-bot"
+ cachix: "misterio"
+
secrets:
- f2907d38-97b4-4e7d-9fb9-57b3fb0135af
+
tasks:
- setup_cachix: |
- cat ~/.cachix_token | cachix authtoken --stdin
- cachix use misterio
+ cachix authtoken --stdin < ~/.cachix_token
+ cachix use "$cachix"
- build: |
- cd sistemer-bot
- nix --quiet build
+ cd "$repo"
+ nix build
- upload_cachix: |
- nix path-info sistemer-bot/result/ -r | cachix push misterio
+ nix path-info "$repo"/result/ -r | cachix push "$cachix"
M Cargo.lock => Cargo.lock +138 -22
@@ 24,6 24,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84450d0b4a8bd1ba4144ce8ce718fbc5d071358b1e5384bace6536b3d1f2d5b3"
[[package]]
+name = "aquamarine"
+version = "0.1.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96e14cb2a51c8b45d26a4219981985c7350fc05eacb7b5b2939bceb2ffefdf3e"
+dependencies = [
+ "itertools",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
name = "async-trait"
version = "0.1.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ 218,6 231,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"
[[package]]
+name = "dptree"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d07c51087ee4ee69519cb965ce56d05fb0ef23de5caffc69778d36b1e99a3e7"
+dependencies = [
+ "futures",
+]
+
+[[package]]
name = "either"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ 246,6 268,16 @@ dependencies = [
]
[[package]]
+name = "erasable"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f11890ce181d47a64e5d1eb4b6caba0e7bae911a356723740d058a5d0340b7d"
+dependencies = [
+ "autocfg",
+ "scopeguard",
+]
+
+[[package]]
name = "fastrand"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ 394,7 426,7 @@ checksum = "418d37c8b1d42553c93648be529cb70f920d3baf8ef469b74b9638df426e0b4c"
dependencies = [
"cfg-if 1.0.0",
"libc",
- "wasi",
+ "wasi 0.10.0+wasi-snapshot-preview1",
]
[[package]]
@@ 423,6 455,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
[[package]]
+name = "heck"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
+
+[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ 539,6 577,15 @@ dependencies = [
]
[[package]]
+name = "indoc"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7906a9fababaeacb774f72410e497a1d18de916322e33797bb2cd29baa23c9e"
+dependencies = [
+ "unindent",
+]
+
+[[package]]
name = "instant"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ 554,6 601,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9"
[[package]]
+name = "itertools"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b"
+dependencies = [
+ "either",
+]
+
+[[package]]
name = "itoa"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ 582,9 638,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
-version = "0.2.112"
+version = "0.2.121"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125"
+checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f"
[[package]]
name = "lock_api"
@@ 649,14 705,15 @@ dependencies = [
[[package]]
name = "mio"
-version = "0.7.14"
+version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc"
+checksum = "52da4364ffb0e4fe33a9841a98a3f3014fb964045ce4f7a45a398243c8d6b0c9"
dependencies = [
"libc",
"log",
"miow",
"ntapi",
+ "wasi 0.11.0+wasi-snapshot-preview1",
"winapi",
]
@@ 849,6 906,30 @@ dependencies = [
]
[[package]]
+name = "proc-macro-error"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
+dependencies = [
+ "proc-macro-error-attr",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro-error-attr"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "version_check",
+]
+
+[[package]]
name = "proc-macro2"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ 873,6 954,15 @@ dependencies = [
]
[[package]]
+name = "rc-box"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e0690759eabf094030c2cdabc25ade1395bac02210d920d655053c1d49583fd8"
+dependencies = [
+ "erasable",
+]
+
+[[package]]
name = "redox_syscall"
version = "0.1.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ 1077,18 1167,18 @@ dependencies = [
[[package]]
name = "sistemer-bot"
-version = "1.1.4"
+version = "2.0.0-pre1"
dependencies = [
"anyhow",
"chrono",
"dotenv",
+ "indoc",
"log",
"pretty_env_logger",
- "regex",
"reqwest",
+ "serde",
"teloxide",
"tokio",
- "unidecode",
]
[[package]]
@@ 1105,9 1195,9 @@ checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
[[package]]
name = "socket2"
-version = "0.4.2"
+version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5dc90fe6c7be1a323296982db1836d1ea9e47b6839496dde9a541bc496df3516"
+checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0"
dependencies = [
"libc",
"winapi",
@@ 1131,14 1221,28 @@ dependencies = [
]
[[package]]
+name = "take_mut"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60"
+
+[[package]]
+name = "takecell"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "20f34339676cdcab560c9a82300c4c2581f68b9369aedf0fae86f2ff9565ff3e"
+
+[[package]]
name = "teloxide"
-version = "0.5.3"
+version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9964854e5ec3a5a44a9f50ebb7641327f1084ab4fc37a6c4a23cc011a388dc2e"
+checksum = "1661e7429fceb92080e41548b2fcc5b9cc38db8740316efbd9b53ba693cac0cf"
dependencies = [
+ "aquamarine",
"async-trait",
"bytes",
"derive_more",
+ "dptree",
"flurry",
"futures",
"log",
@@ 1157,10 1261,11 @@ dependencies = [
[[package]]
name = "teloxide-core"
-version = "0.3.4"
+version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "114c9057a3a2f74d937ece64029b362f583a69fb4b7405722c6dc03cd5bb4658"
+checksum = "f37ad94e503964412cd4894138bf1644454f26060db4fa6bc698befa7d6d6656"
dependencies = [
+ "bitflags",
"bytes",
"chrono",
"derive_more",
@@ 1171,10 1276,13 @@ dependencies = [
"never",
"once_cell",
"pin-project",
+ "rc-box",
"reqwest",
"serde",
"serde_json",
"serde_with_macros",
+ "take_mut",
+ "takecell",
"thiserror",
"tokio",
"tokio-util",
@@ 1184,10 1292,11 @@ dependencies = [
[[package]]
name = "teloxide-macros"
-version = "0.4.1"
+version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2fb7e97b8bef2231aea6643558147c7f9c112675c4ca49f24d8fac2edff1216d"
+checksum = "7d08322f107110dc4aadf5683bf19df9340eebc567529def2d17c830de198a58"
dependencies = [
+ "heck",
"proc-macro2",
"quote",
"syn",
@@ 1243,7 1352,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
dependencies = [
"libc",
- "wasi",
+ "wasi 0.10.0+wasi-snapshot-preview1",
"winapi",
]
@@ 1264,9 1373,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]]
name = "tokio"
-version = "1.15.0"
+version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fbbf1c778ec206785635ce8ad57fe52b3009ae9e0c9f574a728f3049d3e55838"
+checksum = "2af73ac49756f3f7c01172e34a23e5d0216f6c32333757c2c61feb2bbff5a5ee"
dependencies = [
"bytes",
"libc",
@@ 1276,6 1385,7 @@ dependencies = [
"once_cell",
"pin-project-lite",
"signal-hook-registry",
+ "socket2",
"tokio-macros",
"winapi",
]
@@ 1389,10 1499,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
-name = "unidecode"
-version = "0.3.0"
+name = "unindent"
+version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "402bb19d8e03f1d1a7450e2bd613980869438e0666331be3e073089124aa1adc"
+checksum = "514672a55d7380da379785a4d70ca8386c8883ff7eaae877be4d2081cebe73d8"
[[package]]
name = "url"
@@ 1445,6 1555,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
name = "wasm-bindgen"
version = "0.2.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
M Cargo.toml => Cargo.toml +6 -6
@@ 1,6 1,6 @@
[package]
name = "sistemer-bot"
-version = "1.1.4"
+version = "2.0.0-pre1"
description = "Telegram bot for BSI 020"
edition = "2018"
license = "GPL-3.0-or-later"
@@ 9,13 9,13 @@ homepage = "https://misterio.me"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
-teloxide = { version = "0.5", features = ["auto-send", "macros"] }
+teloxide = { version = "0.7.2", features = ["auto-send", "macros"] }
log = "0.4"
-pretty_env_logger = "0.4.0"
-tokio = { version = "1.8", features = ["rt-multi-thread", "macros"] }
+pretty_env_logger = "0.4"
+tokio = { version = "1.17", features = ["rt-multi-thread", "macros"] }
dotenv = "0.15"
reqwest = "0.11"
anyhow = "1.0"
-regex = "1.5"
chrono = "0.4"
-unidecode = "0.3"
+serde = { version = "1.0", features = ["derive"] }
+indoc = "1.0"
M flake.lock => flake.lock +6 -6
@@ 2,11 2,11 @@
"nodes": {
"nixpkgs": {
"locked": {
- "lastModified": 1642104392,
- "narHash": "sha256-m71b7MgMh9FDv4MnI5sg9MiBVW6DhE1zq+d/KlLWSC8=",
+ "lastModified": 1648390671,
+ "narHash": "sha256-u69opCeHUx3CsdIerD0wVSR+DjfDQjnztObqfk9Trqc=",
"owner": "NixOS",
"repo": "nixpkgs",
- "rev": "5aaed40d22f0d9376330b6fa413223435ad6fee5",
+ "rev": "ce8cbe3c01fd8ee2de526ccd84bbf9b82397a510",
"type": "github"
},
"original": {
@@ 24,11 24,11 @@
},
"utils": {
"locked": {
- "lastModified": 1638122382,
- "narHash": "sha256-sQzZzAbvKEqN9s0bzWuYmRaA03v40gaJ4+iL1LXjaeI=",
+ "lastModified": 1648297722,
+ "narHash": "sha256-W+qlPsiZd8F3XkzXOzAoR+mpFqzm3ekQkJNa+PIh1BQ=",
"owner": "numtide",
"repo": "flake-utils",
- "rev": "74f7e4319258e287b0f9cb95426c9853b282730b",
+ "rev": "0f8662f1319ad6abf89b3380dd2722369fc51ade",
"type": "github"
},
"original": {
D src/agora.rs => src/agora.rs +0 -19
@@ 1,19 0,0 @@
-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())
-}
D src/disciplina.rs => src/disciplina.rs +0 -29
@@ 1,29 0,0 @@
-use anyhow::Result;
-use regex::Regex;
-
-pub async fn get_disciplina(disciplina: &str) -> Result<String> {
- let fulltext = reqwest::get("https://misterio.me/notes/bsi/disciplinas-2021-2.html")
- .await?
- .text()
- .await?;
-
- let start_pattern = Regex::new(&format!("<h2.*{}.*</h2>", unidecode::unidecode(&disciplina.to_lowercase())))?;
- let stop_pattern = Regex::new("<hr />")?;
-
- let mut output: String = "".into();
- let mut adding = false;
- for line in fulltext.lines() {
- if start_pattern.is_match(&unidecode::unidecode(&line.to_lowercase())) {
- output.push_str(&crate::sanitize_line(line, false)?);
- adding = true;
- } else if adding {
- if stop_pattern.is_match(line) {
- return Ok(output);
- } else {
- output.push_str(&crate::sanitize_line(line, false)?);
- }
- }
- }
-
- Ok("Disciplina não encontrada!".into())
-}
D src/disciplinas.rs => src/disciplinas.rs +0 -20
@@ 1,20 0,0 @@
-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)
-}
D src/hoje.rs => src/hoje.rs +0 -41
@@ 1,41 0,0 @@
-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)
-}
D src/horarios.rs => src/horarios.rs +0 -28
@@ 1,28 0,0 @@
-use anyhow::Result;
-use regex::Regex;
-
-pub async fn get_horarios() -> Result<String> {
- let fulltext = reqwest::get("https://misterio.me/notes/bsi/disciplinas-2021-2.html")
- .await?
- .text()
- .await?;
-
- let start_pattern = Regex::new("<h2 id=\"horários\">")?;
- let stop_pattern = Regex::new("<hr />")?;
-
- let mut output: String = "".into();
- let mut adding = false;
- for line in fulltext.lines() {
- if start_pattern.is_match(&line.to_lowercase()) {
- output.push_str(&crate::sanitize_line(line, false)?);
- adding = true;
- } else if adding {
- if stop_pattern.is_match(line) {
- return Ok(output);
- } else {
- output.push_str(&crate::sanitize_line(line, false)?);
- }
- }
- }
- Ok(output)
-}
M src/lib.rs => src/lib.rs +194 -43
@@ 1,44 1,195 @@
-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 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");
- // Remove <ul> entire lines
- let line = Regex::new("^ *</?ul>\n")?.replace_all(&line, "");
- let line = Regex::new("</?ul>")?.replace_all(&line, "");
- // Remove <li>
- let line = Regex::new("^ *</?li>\n")?.replace_all(&line, "");
- let line = Regex::new("</?li>")?.replace_all(&line, "");
- // Remove <p>
- let line = Regex::new("</?p>")?.replace_all(&line, "");
- // 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 = if remove_bold {
- Regex::new("<b.*>(.*)</b>")?.replace(&line, "$1")
- } else {
- line
- };
-
- Ok(line.into())
+pub use anyhow::Result;
+
+use anyhow::anyhow;
+use indoc::formatdoc;
+use serde::Deserialize;
+const URL: &str = "http://127.0.0.1:4000";
+
+#[derive(Deserialize)]
+pub struct Disciplina {
+ codigo: String,
+ nome: String,
+ #[serde(default)]
+ optativa: bool,
+ professor: Option<Professor>,
+ plataforma: Option<Informacao>,
+ presenca: Option<Informacao>,
+ sala: Option<Informacao>,
+ avaliacoes: Option<Avaliacoes>,
+}
+
+impl Disciplina {
+ pub async fn listar_disciplinas(turma: &str) -> Result<Vec<Self>> {
+ let url = format!("{}/disciplinas/{}.json", URL, turma);
+ let response = reqwest::get(&url).await?;
+
+ let disciplinas = response.json().await?;
+ Ok(disciplinas)
+ }
+ pub async fn buscar_disciplina(turma: &str, busca: &str) -> Result<Self> {
+ let disciplinas = Self::listar_disciplinas(turma).await?;
+ let encontrado = disciplinas
+ .into_iter()
+ .find(|x| x.codigo == busca)
+ .ok_or(anyhow!("Disciplina não encontrada"))?;
+ Ok(encontrado)
+ }
+
+ fn titulo(&self) -> String {
+ let codigo = &self.codigo;
+ let nome = &self.nome;
+ format!("*{codigo} - {nome}*")
+ }
+
+ fn sobre(&self) -> String {
+ let tipo = if self.optativa {
+ "optativa"
+ } else {
+ "obrigatória"
+ };
+ let prof = self
+ .professor
+ .as_ref()
+ .map(|p| p.info())
+ .unwrap_or("TBD".into());
+ let plataforma = self
+ .plataforma
+ .as_ref()
+ .map(|p| p.info())
+ .unwrap_or("TBD".into());
+ let sala = self.sala.as_ref().map(|p| p.info()).unwrap_or("TBD".into());
+ let presenca = self
+ .presenca
+ .as_ref()
+ .map(|p| p.info())
+ .unwrap_or("TBD".into());
+
+ formatdoc!(
+ "
+ *Sobre*
+ _Tipo_: {tipo}
+ _Prof_: {prof}
+ _Plataforma_: {plataforma}
+ _Sala_: {sala}
+ _Presença_: {presenca}"
+ )
+ }
+
+ pub fn info(&self) -> String {
+ let titulo = self.titulo();
+ let sobre = self.sobre();
+ let avaliacoes = self
+ .avaliacoes
+ .as_ref()
+ .map(|a| a.info())
+ .unwrap_or_default();
+
+ formatdoc!(
+ "
+ {titulo}
+
+ {sobre}
+ {}{avaliacoes}",
+ if avaliacoes.is_empty() { "" } else { "\n" }
+ )
+ }
+}
+
+#[derive(Deserialize)]
+struct Professor {
+ nome: String,
+ email: String,
+}
+
+impl Professor {
+ pub fn info(&self) -> String {
+ format!("{} ({})", self.nome, self.email)
+ }
+}
+
+#[derive(Deserialize)]
+struct Informacao {
+ info: String,
+ url: Option<String>,
+}
+
+impl Informacao {
+ pub fn info(&self) -> String {
+ match &self.url {
+ Some(url) => format!("[{}]({})", self.info, url),
+ None => format!("{}", self.info),
+ }
+ }
+}
+
+#[derive(Deserialize)]
+struct Avaliacoes {
+ criterio: Option<String>,
+ provas: Vec<Avaliacao>,
+ atividades: Vec<Avaliacao>,
+}
+impl Avaliacoes {
+ pub fn info(&self) -> String {
+ let provas = self
+ .provas
+ .iter()
+ .map(|x| format!(" {}", x.info()))
+ .collect::<Vec<String>>()
+ .join("\n");
+
+ let atividades = self
+ .atividades
+ .iter()
+ .map(|x| format!(" {}", x.info()))
+ .collect::<Vec<String>>()
+ .join("\n");
+
+ let criterio = self.criterio.as_deref().unwrap_or("TBD");
+
+ formatdoc!(
+ "
+ _Criterio_: {criterio}
+ _Provas_:
+ {provas}
+ _Atividades_:
+ {atividades}"
+ )
+ }
+}
+
+#[derive(Deserialize)]
+struct Avaliacao {
+ nome: String,
+ data: Option<String>,
+ assunto: Option<String>,
+}
+
+impl Avaliacao {
+ pub fn info(&self) -> String {
+ let nome = &self.nome;
+ let data = match &self.data {
+ Some(d) => format!(": {d}"),
+ None => "".into(),
+ };
+ let assunto = match &self.assunto {
+ Some(a) => format!("- {a}"),
+ None => "".into(),
+ };
+ format!("{nome}{data}{assunto}")
+ }
+}
+
+#[derive(Deserialize)]
+struct Aulas {
+ segunda: Option<Aula>,
+ terca: Option<Aula>,
+ quarta: Option<Aula>,
+ quinta: Option<Aula>,
+ sexta: Option<Aula>,
+}
+
+#[derive(Deserialize)]
+struct Aula {
+ inicio: String,
+ fim: String,
}
M src/main.rs => src/main.rs +4 -104
@@ 1,107 1,7 @@
-use teloxide::prelude::*;
-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::{agora, disciplina, disciplinas, hoje, horarios, proxima};
-
-#[derive(BotCommand)]
-#[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_command(cx: UpdateWithCx<AutoSend<Bot>, Message>, command: Command) -> Result<()> {
- match command {
- Command::Help => cx.answer(Command::descriptions()).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(())
-}
-
-async fn run() {
- teloxide::enable_logging!();
-
- let bot = Bot::from_env().auto_send();
- let bot_name: String = "Sistemer_Bot".into();
-
- teloxide::commands_repl(bot, bot_name, answer_command).await;
-}
+use sistemer_bot::{Disciplina, Result};
#[tokio::main]
-async fn main() {
- dotenv().ok();
- run().await;
+async fn main() -> Result<()> {
+ print!("{}", Disciplina::buscar_disciplina("bsi020", "SCC0541").await?.info());
+ Ok(())
}
D src/proxima.rs => src/proxima.rs +0 -19
@@ 1,19 0,0 @@
-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())
-}