Files
altherego/src/main.rs

167 lines
4.9 KiB
Rust

use envconfig::Envconfig;
use log::{debug, info, warn};
use teloxide::{prelude::*, utils::command::BotCommand};
const VERSION: &str = env!("GIT_REVISION");
const BRANCH: &str = env!("GIT_BRANCH");
#[tokio::main]
async fn main() {
debug!("starting the application");
tokio::spawn(run()).await.unwrap();
}
#[derive(Envconfig, Clone)]
struct Settings {
#[envconfig(from = "ALTEREGO_TELEGRAM_TOKEN")]
pub telegram_token: String,
#[envconfig(
from = "ALTEREGO_CLIMATE_DSN",
default = "http://127.0.0.1:18081/v1/home/temperature"
)]
pub climate_dsn: String,
#[envconfig(
from = "ALTEREGO_HOSTTEMP_CMD",
default = "/opt/vc/bin/vcgencmd measure_temp"
)]
pub hosttemp_cmd: String,
}
async fn run() {
env_logger::init();
let settings = Settings::init_from_env().expect("reading config values");
let startup = std::sync::Arc::from(std::time::SystemTime::now());
let bot = teloxide::Bot::builder()
.token(&settings.telegram_token)
.build();
let bot_name = "AlterEgo";
teloxide::commands_repl(bot, bot_name, move |cx, command| {
let climate = settings.climate_dsn.clone();
let cmd: String = settings.hosttemp_cmd.clone();
let cmd: Vec<&str> = cmd.split(' ').collect();
let console_cmd = cmd.first().expect("getting console command").to_string();
let arg: String = cmd.get(1).unwrap_or(&"").to_string();
let startup = *startup;
async move { handler(cx, command, climate, console_cmd, arg, startup).await }
})
.await;
}
#[derive(serde::Deserialize, Debug)]
struct Climate {
humidity: f32,
temp: f32,
}
async fn handler(
cx: UpdateWithCx<Message>,
command: Command,
dsn: String,
console_command: String,
console_arg: String,
startup: std::time::SystemTime,
) -> ResponseResult<()> {
let request_id = uuid::Uuid::new_v4();
info!(
"incoming request xreqid={} command={:?}",
request_id, command
);
match command {
Command::Help => cx.answer(Command::descriptions()).send().await?,
Command::HostTemperature => {
info!(
"querying command {} with arg {}",
console_command, console_arg
);
let cmd = std::process::Command::new(&console_command)
.arg(&console_arg)
.stdout(std::process::Stdio::piped())
.spawn()
.expect("running vcgencmd command");
let output = cmd.wait_with_output().expect("waiting for output");
let parsed =
std::string::String::from_utf8(output.stdout).expect("casting into string");
let parsed = parsed.replace("temp=", "");
cx.answer_str(format!("Your Raspberry PI temperature is {}", parsed))
.await?
}
Command::RoomTemperature => {
info!("sending request to {}", dsn);
let response = match reqwest::get(&dsn).await {
Ok(response) => response,
Err(err) => {
warn!(
"unable to handle request xreqid={} error={:?}",
request_id, err
);
cx.answer_str(format!("something went wrong, reference to {}", request_id))
.await?;
return Err(RequestError::NetworkError(err));
}
};
let info: Climate = match response.json::<Climate>().await {
Ok(result) => result,
Err(err) => {
warn!(
"unable to handle request xreqid={} error={:?}",
request_id, err
);
cx.answer_str(format!("something went wrong, reference to {}", request_id))
.await?;
return Err(RequestError::NetworkError(err));
}
};
debug!("parsed value: {:?}", info);
cx.answer_str(format!(
"Your room temperature is {:.2} and humidity is {:.2}.",
info.temp, info.humidity
))
.await?
}
Command::VersionRequest => {
cx.answer_str(format!(
"app version is {}@{}, uptime is {} second(-s)",
VERSION,
BRANCH,
startup.elapsed().unwrap().as_secs()
))
.await?
}
};
Ok(())
}
#[derive(BotCommand, Debug)]
#[command(rename = "lowercase", description = "These commands are supported:")]
enum Command {
#[command(description = "display this text.")]
Help,
#[command(description = "temperature of your room.")]
RoomTemperature,
#[command(description = "temperature of raspberry.")]
HostTemperature,
#[command(description = "prints current version.")]
VersionRequest,
}