|
|
|
@ -1,29 +1,33 @@
|
|
|
|
|
use crate::{config::Host, wol};
|
|
|
|
|
|
|
|
|
|
use std::time::Duration;
|
|
|
|
|
use std::{collections::HashMap, str::FromStr, time::Duration};
|
|
|
|
|
|
|
|
|
|
use tracing::{error, info};
|
|
|
|
|
|
|
|
|
|
use async_trait::async_trait;
|
|
|
|
|
use matrix_sdk::{
|
|
|
|
|
self,
|
|
|
|
|
identifiers::RoomId,
|
|
|
|
|
events::{
|
|
|
|
|
room::{
|
|
|
|
|
member::MemberEventContent,
|
|
|
|
|
message::{MessageEvent, MessageEventContent, TextMessageEventContent},
|
|
|
|
|
message::{MessageEvent, MessageEventContent, TextMessageEventContent, NoticeMessageEventContent},
|
|
|
|
|
},
|
|
|
|
|
stripped::StrippedStateEvent,
|
|
|
|
|
},
|
|
|
|
|
Client, EventEmitter, SyncRoom,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
use lazy_static::lazy_static;
|
|
|
|
|
use regex::Regex;
|
|
|
|
|
|
|
|
|
|
pub(crate) struct WakeOnLanBot {
|
|
|
|
|
pub(crate) client: Client,
|
|
|
|
|
pub(crate) hosts: Vec<Host>,
|
|
|
|
|
pub(crate) hosts: HashMap<String, Host>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl WakeOnLanBot {
|
|
|
|
|
pub fn new(client: Client, hosts: Vec<Host>) -> Self {
|
|
|
|
|
pub fn new(client: Client, hosts: HashMap<String, Host>) -> Self {
|
|
|
|
|
Self { client, hosts }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -66,7 +70,7 @@ impl EventEmitter for WakeOnLanBot {
|
|
|
|
|
}
|
|
|
|
|
async fn on_room_message(&self, room: SyncRoom, event: &MessageEvent) {
|
|
|
|
|
match room {
|
|
|
|
|
SyncRoom::Joined(_room) => {
|
|
|
|
|
SyncRoom::Joined(room) => {
|
|
|
|
|
let msg_body = if let MessageEvent {
|
|
|
|
|
content:
|
|
|
|
|
MessageEventContent::Text(TextMessageEventContent { body: msg_body, .. }),
|
|
|
|
@ -78,25 +82,70 @@ impl EventEmitter for WakeOnLanBot {
|
|
|
|
|
String::new()
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
for host in &self.hosts {
|
|
|
|
|
//TODO: pull the host name out of the message and check it. If the hostname is
|
|
|
|
|
//invalid, complain
|
|
|
|
|
if msg_body == format!("!wake {}", host.name) {
|
|
|
|
|
if host.users.contains(&event.sender.to_string()) {
|
|
|
|
|
info!("Waking host {}", host.name);
|
|
|
|
|
//TODO: reply here
|
|
|
|
|
match wol::wake(host.mac_addr) {
|
|
|
|
|
Ok(()) => info!("Magic packet sent to {} successfully", host.name),
|
|
|
|
|
Err(e) => error!(
|
|
|
|
|
"Couldn't send magic packet to {}, error: {}",
|
|
|
|
|
host.name, e
|
|
|
|
|
),
|
|
|
|
|
}
|
|
|
|
|
if let Ok(command) = Command::from_str(&msg_body) {
|
|
|
|
|
handle_command(command, &self.client, &self.hosts, event, &room.read().await.room_id.clone()).await;
|
|
|
|
|
} else {
|
|
|
|
|
//TODO: give help text
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
_ => {}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn handle_command(command: Command, client: &Client, hosts: &HashMap<String, Host>, event: &MessageEvent, room: &RoomId) {
|
|
|
|
|
match command {
|
|
|
|
|
Command::Wake { host } => {
|
|
|
|
|
if let Some(host_conf) = hosts.get(&host) {
|
|
|
|
|
if host_conf.users.contains(&event.sender.to_string()) {
|
|
|
|
|
info!("Waking host {}", host);
|
|
|
|
|
match wol::wake(host_conf.mac_addr) {
|
|
|
|
|
Ok(()) => {
|
|
|
|
|
info!("Magic packet sent to {} successfully", host);
|
|
|
|
|
send_message(client, &format!("Successfully send magic packet to {}", host), room).await;
|
|
|
|
|
}
|
|
|
|
|
Err(e) => {
|
|
|
|
|
error!("Couldn't send magic packet to {}, error: {}", host, e);
|
|
|
|
|
send_message(client, &format!("Failed to send magic packet to {}", host), room).await;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
send_message(client, &format!("No permission to wake up {}!", host), room).await;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
send_message(client, &format!("Host {} not found!", host), room).await;
|
|
|
|
|
}
|
|
|
|
|
_ => {}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn send_message(client: &Client, message: &str, room: &RoomId) {
|
|
|
|
|
client.room_send(room, MessageEventContent::Notice(NoticeMessageEventContent {
|
|
|
|
|
body: message.to_owned(),
|
|
|
|
|
format: None,
|
|
|
|
|
formatted_body: None,
|
|
|
|
|
relates_to: None,
|
|
|
|
|
}), None).await.unwrap(); //TODO error handling here
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum Command {
|
|
|
|
|
Wake { host: String },
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl FromStr for Command {
|
|
|
|
|
type Err = &'static str;
|
|
|
|
|
|
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
|
|
|
lazy_static! {
|
|
|
|
|
static ref RE_WAKE: Regex = Regex::new("!wake (?P<host>.*)").unwrap();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if let Some(captures) = RE_WAKE.captures(s) {
|
|
|
|
|
return Ok(Self::Wake {
|
|
|
|
|
host: captures["host"].to_string(),
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
return Err("Invalid command");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|