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.
# 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]]
name = "ansi_term"
version = "0.11.0"
@ -565,14 +574,15 @@ dependencies = [
"chrono",
"clap",
"fern",
"lazy_static",
"log",
"matrix-sdk",
"regex",
"serde",
"tokio",
"toml",
"tracing",
"url",
"wole",
]
[[package]]
@ -834,6 +844,24 @@ version = "0.1.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "remove_dir_all"
version = "0.5.3"
@ -1171,6 +1199,15 @@ dependencies = [
"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]]
name = "time"
version = "0.1.44"
@ -1532,12 +1569,6 @@ dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "wole"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "130532c58c60bb816f30c3975734d9c4cb78981213b6dbcf19ba86539ed36e80"
[[package]]
name = "ws2_32-sys"
version = "0.2.1"

View File

@ -9,7 +9,6 @@ license = "AGPL-3.0-only"
[dependencies]
matrix-sdk = { version = "0.1.0", default_features = false, features = ["messages"] }
wole = "0.1.3"
fern = "0.6.0"
chrono = "0.4.19"
tracing = { version = "0.1.21", features = ["log"] }
@ -21,4 +20,5 @@ toml = "0.5.7"
serde = { version = "1.0.116", features = ["derive"] }
anyhow = "1.0.33"
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 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");
}
}
}

View File

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