feat: reply to commands

Reply to commands in addition to executing them.
This commit is contained in:
Jan Christian Grünhage 2020-10-15 23:49:58 +02:00
parent 3eb8ae7ddb
commit 52257759cb
4 changed files with 112 additions and 32 deletions

45
Cargo.lock generated
View File

@ -1,5 +1,14 @@
# This file is automatically @generated by Cargo. # This file is automatically @generated by Cargo.
# It is not intended for manual editing. # It is not intended for manual editing.
[[package]]
name = "aho-corasick"
version = "0.7.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b476ce7103678b0c6d3d395dbbae31d48ff910bd28be979ba5d48c6351131d0d"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "ansi_term" name = "ansi_term"
version = "0.11.0" version = "0.11.0"
@ -565,14 +574,15 @@ dependencies = [
"chrono", "chrono",
"clap", "clap",
"fern", "fern",
"lazy_static",
"log", "log",
"matrix-sdk", "matrix-sdk",
"regex",
"serde", "serde",
"tokio", "tokio",
"toml", "toml",
"tracing", "tracing",
"url", "url",
"wole",
] ]
[[package]] [[package]]
@ -834,6 +844,24 @@ version = "0.1.57"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
[[package]]
name = "regex"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8963b85b8ce3074fecffde43b4b0dded83ce2f367dc8d363afc56679f3ee820b"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
"thread_local",
]
[[package]]
name = "regex-syntax"
version = "0.6.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8cab7a364d15cde1e505267766a2d3c4e22a843e1a601f0fa7564c0f82ced11c"
[[package]] [[package]]
name = "remove_dir_all" name = "remove_dir_all"
version = "0.5.3" version = "0.5.3"
@ -1171,6 +1199,15 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "thread_local"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
dependencies = [
"lazy_static",
]
[[package]] [[package]]
name = "time" name = "time"
version = "0.1.44" version = "0.1.44"
@ -1532,12 +1569,6 @@ dependencies = [
"winapi 0.3.9", "winapi 0.3.9",
] ]
[[package]]
name = "wole"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "130532c58c60bb816f30c3975734d9c4cb78981213b6dbcf19ba86539ed36e80"
[[package]] [[package]]
name = "ws2_32-sys" name = "ws2_32-sys"
version = "0.2.1" version = "0.2.1"

View File

@ -9,7 +9,6 @@ license = "AGPL-3.0-only"
[dependencies] [dependencies]
matrix-sdk = { version = "0.1.0", default_features = false, features = ["messages"] } matrix-sdk = { version = "0.1.0", default_features = false, features = ["messages"] }
wole = "0.1.3"
fern = "0.6.0" fern = "0.6.0"
chrono = "0.4.19" chrono = "0.4.19"
tracing = { version = "0.1.21", features = ["log"] } tracing = { version = "0.1.21", features = ["log"] }
@ -21,4 +20,5 @@ toml = "0.5.7"
serde = { version = "1.0.116", features = ["derive"] } serde = { version = "1.0.116", features = ["derive"] }
anyhow = "1.0.33" anyhow = "1.0.33"
clap = "2.33.3" clap = "2.33.3"
matrix-wol = { path = "." } regex = "1.4.1"
lazy_static = "1.4.0"

View File

@ -1,29 +1,33 @@
use crate::{config::Host, wol}; use crate::{config::Host, wol};
use std::time::Duration; use std::{collections::HashMap, str::FromStr, time::Duration};
use tracing::{error, info}; use tracing::{error, info};
use async_trait::async_trait; use async_trait::async_trait;
use matrix_sdk::{ use matrix_sdk::{
self, self,
identifiers::RoomId,
events::{ events::{
room::{ room::{
member::MemberEventContent, member::MemberEventContent,
message::{MessageEvent, MessageEventContent, TextMessageEventContent}, message::{MessageEvent, MessageEventContent, TextMessageEventContent, NoticeMessageEventContent},
}, },
stripped::StrippedStateEvent, stripped::StrippedStateEvent,
}, },
Client, EventEmitter, SyncRoom, Client, EventEmitter, SyncRoom,
}; };
use lazy_static::lazy_static;
use regex::Regex;
pub(crate) struct WakeOnLanBot { pub(crate) struct WakeOnLanBot {
pub(crate) client: Client, pub(crate) client: Client,
pub(crate) hosts: Vec<Host>, pub(crate) hosts: HashMap<String, Host>,
} }
impl WakeOnLanBot { impl WakeOnLanBot {
pub fn new(client: Client, hosts: Vec<Host>) -> Self { pub fn new(client: Client, hosts: HashMap<String, Host>) -> Self {
Self { client, hosts } Self { client, hosts }
} }
} }
@ -66,7 +70,7 @@ impl EventEmitter for WakeOnLanBot {
} }
async fn on_room_message(&self, room: SyncRoom, event: &MessageEvent) { async fn on_room_message(&self, room: SyncRoom, event: &MessageEvent) {
match room { match room {
SyncRoom::Joined(_room) => { SyncRoom::Joined(room) => {
let msg_body = if let MessageEvent { let msg_body = if let MessageEvent {
content: content:
MessageEventContent::Text(TextMessageEventContent { body: msg_body, .. }), MessageEventContent::Text(TextMessageEventContent { body: msg_body, .. }),
@ -78,25 +82,70 @@ impl EventEmitter for WakeOnLanBot {
String::new() String::new()
}; };
for host in &self.hosts { if let Ok(command) = Command::from_str(&msg_body) {
//TODO: pull the host name out of the message and check it. If the hostname is handle_command(command, &self.client, &self.hosts, event, &room.read().await.room_id.clone()).await;
//invalid, complain } else {
if msg_body == format!("!wake {}", host.name) { //TODO: give help text
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
),
}
}
}
} }
} }
_ => {} _ => {}
} }
} }
} }
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");
}
}
}

View File

@ -1,6 +1,7 @@
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use clap::{clap_app, crate_authors, crate_description, crate_name, crate_version}; use clap::{clap_app, crate_authors, crate_description, crate_name, crate_version};
use serde::Deserialize; use serde::Deserialize;
use std::collections::HashMap;
use tracing::info; use tracing::info;
#[derive(Deserialize)] #[derive(Deserialize)]
@ -8,12 +9,11 @@ pub(crate) struct Config {
pub(crate) hs_url: String, pub(crate) hs_url: String,
pub(crate) username: String, pub(crate) username: String,
pub(crate) password: String, pub(crate) password: String,
pub(crate) hosts: Vec<Host>, pub(crate) hosts: HashMap<String, Host>,
} }
#[derive(Deserialize)] #[derive(Deserialize)]
pub(crate) struct Host { pub(crate) struct Host {
pub(crate) name: String,
pub(crate) mac_addr: [u8; 6], pub(crate) mac_addr: [u8; 6],
pub(crate) users: Vec<String>, pub(crate) users: Vec<String>,
} }