summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSanto Cariotti <santo@dcariotti.me>2021-08-04 18:09:12 +0200
committerSanto Cariotti <santo@dcariotti.me>2021-08-04 18:09:12 +0200
commit51ea2896cdcdf7b6027f96421a0ce2b454486e03 (patch)
tree22eaae83fd2655cad9bc477bc59ce3fb92539788 /src
parent7c445e6458b6b209ce59ed89dbb73daa96e2199f (diff)
docs: init adds
Diffstat (limited to 'src')
-rw-r--r--src/browser/mod.rs5
-rw-r--r--src/browser/web_browser.rs11
-rw-r--r--src/commands.rs18
-rw-r--r--src/config.rs4
-rw-r--r--src/main.rs3
5 files changed, 41 insertions, 0 deletions
diff --git a/src/browser/mod.rs b/src/browser/mod.rs
index d5ec337..7205f27 100644
--- a/src/browser/mod.rs
+++ b/src/browser/mod.rs
@@ -6,12 +6,16 @@ use thirtyfour::prelude::WebDriverResult;
mod web_browser;
+/// Create a new instance of `Browser` and associate it with the static variable `WEB_BROWSER`.
+/// This is an unsecure type of usage, so the block is inside the `unsafe` block
pub async fn init(driver_url: &String) {
unsafe {
WEB_BROWSER = Some(Browser::new(driver_url).await);
}
}
+/// Login using the credentials from the `Config`. 'Cause its kind of nature
+/// this is an `unsafe` block, so the function is defined like that
pub async unsafe fn login(credentials: &Config) -> WebDriverResult<()> {
if let Some(driver) = &WEB_BROWSER {
driver._login(credentials).await?;
@@ -20,6 +24,7 @@ pub async unsafe fn login(credentials: &Config) -> WebDriverResult<()> {
Ok(())
}
+/// Get the faculties available for booking a room
pub async unsafe fn get_faculties() -> WebDriverResult<Option<HashMap<String, String>>> {
if let Some(driver) = &WEB_BROWSER {
if let Some(faculties) = driver.faculties().await? {
diff --git a/src/browser/web_browser.rs b/src/browser/web_browser.rs
index d2ba6c4..c7497cf 100644
--- a/src/browser/web_browser.rs
+++ b/src/browser/web_browser.rs
@@ -6,14 +6,19 @@ use thirtyfour::error::{WebDriverError, WebDriverErrorInfo, WebDriverErrorValue}
use thirtyfour::prelude::{By, WebDriverResult};
use thirtyfour::{FirefoxCapabilities, WebDriver, WebDriverCommands};
+/// This url is used to make the login
const LOGIN_URL: &str = "https://studenti.smartedu.unict.it/WorkFlow2011/Logon/Logon.aspx?ReturnUrl=%2fStudenti%2fDefault.aspx";
+/// This url is used to go to the page where a student can book a room for study
const ROOMS_URL: &str = "https://studenti.smartedu.unict.it/StudentSpaceReserv?Type=unaTantum";
+/// Browser struct
pub struct Browser {
+ /// The driver for Firefox, it could be `None`
driver: Option<WebDriver>,
}
impl Browser {
+ /// Create a new `Browser` with a Firefox driver with a personalized User-Agent
pub async fn new(driver_url: &String) -> Self {
let user_agent =
"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:90.0) Gecko/20100101 Firefox/90.0";
@@ -34,6 +39,7 @@ impl Browser {
// Ok(())
// }
+ /// Login on `LOGIN_URL`
pub async unsafe fn _login(&self, credentials: &Config) -> WebDriverResult<()> {
if let Some(_d) = &self.driver {
_d.get(LOGIN_URL).await?;
@@ -57,6 +63,8 @@ impl Browser {
thread::sleep(time::Duration::from_millis(2000));
+ // If the current url is the same as `LOGIN_URL` it means the login didn't work, so
+ // returns a "login error"
if _d.current_url().await? == LOGIN_URL {
return Err(WebDriverError::SessionNotCreated(WebDriverErrorInfo {
status: 400,
@@ -74,6 +82,8 @@ impl Browser {
Ok(())
}
+ /// Get all faculties for booking and return an `HashMap<key, value>` when `key` is the
+ /// key for that faculty inside the `select` tag and `value` is just the text of the option.
pub async fn faculties(&self) -> WebDriverResult<Option<HashMap<String, String>>> {
if let Some(_d) = &self.driver {
_d.get(ROOMS_URL).await?;
@@ -106,4 +116,5 @@ impl Browser {
}
}
+/// The static unsafe variable used to open a web browser
pub static mut WEB_BROWSER: Option<Browser> = None;
diff --git a/src/commands.rs b/src/commands.rs
index 2653600..7ee8d83 100644
--- a/src/commands.rs
+++ b/src/commands.rs
@@ -15,11 +15,18 @@ pub enum Command {
Room,
}
+/// This is the handler for the commands.
+/// It's called by `teloxide` every time someone write `/<text>`.
+///
+/// First it checks if the author is authorized to do the commands. Then match that text thanks to
+/// the `parse` method of `BotCommand`.
pub async fn handler(
cx: UpdateWithCx<AutoSend<Bot>, Message>,
) -> Result<(), Box<dyn Error + Send + Sync>> {
+ // In the config file there's a key for the Telegram username authorized to use this bot.
let username = Config::from_env().unwrap().username;
+ // Compare the author of the message with the Telegram username from the config
if let Some(author) = &cx.update.from().unwrap().username {
if *author != username {
cx.reply_to("You are not allowed to do this action!")
@@ -37,10 +44,16 @@ pub async fn handler(
if let Ok(command) = BotCommand::parse(txt.unwrap(), "unict-reservation") {
match command {
Command::Help => {
+ // Just send the descriptions of all commands
cx.answer(Command::descriptions()).await?;
}
Command::Room => {
let faculties;
+
+ // This is an array of array because the `InlineKeyboardMarkup`
+ // considers each array as a row.
+ // So, using this format Vec<Vec<..>> will print a button
+ // in `n` different rows in only 1 column.
let mut faculties_array: Vec<Vec<InlineKeyboardButton>> = vec![];
unsafe {
faculties = browser::get_faculties().await.unwrap();
@@ -54,12 +67,17 @@ pub async fn handler(
)]);
}
} else {
+ // If the response of the Option `faculties` is None, just answer
+ // an useless button with a text.
+ // I still don't know if it's a good idea to use a callback instead of
+ // a normal text button, but I could handle any such kind of callback
faculties_array.push(vec![InlineKeyboardButton::callback(
"No such element".to_string(),
"".into(),
)]);
}
+ // The `new` method accepts an interator
let keyboard = InlineKeyboardMarkup::new(faculties_array);
cx.answer("Where?").reply_markup(keyboard).await?;
}
diff --git a/src/config.rs b/src/config.rs
index 668dda7..f03a09b 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -3,9 +3,13 @@ use serde::Deserialize;
#[derive(Deserialize, Debug)]
pub struct Config {
+ /// The ID for every italian person, it's used from Smartedu as username
pub cf: String,
+ /// The password of Smartedu
pub password: String,
+ /// Driver url, an example is `http://localhost:4444` for geckodriver
pub driver_url: String,
+ /// Username of the Telegram user authorized to use the bot
pub username: String,
}
diff --git a/src/main.rs b/src/main.rs
index 270af8e..c038a9b 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -17,13 +17,16 @@ async fn main() -> Result<(), Box<dyn Error>> {
let config = Config::from_env().unwrap();
unsafe {
+ // Open the browser
browser::init(&config.driver_url).await;
+ // Login using the credentials inside the `config`
match browser::login(&config).await {
Ok(_) => {
log::info!("Logged in Smartedu");
}
Err(e) => {
+ // Using the bot when the user is not logged in, is simply useless.
panic!("You can't connect: `{}`, credentials are {:?}", e, config);
}
}