feat: reply to commands
Reply to commands in addition to executing them.
This commit is contained in:
parent
3eb8ae7ddb
commit
52257759cb
4 changed files with 112 additions and 32 deletions
45
Cargo.lock
generated
45
Cargo.lock
generated
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
91
src/bot.rs
91
src/bot.rs
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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>,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue