Compare commits

..

3 commits

Author SHA1 Message Date
Jan Christian Grünhage 48a5972aaf chore: update changelog for initial release 2020-10-16 00:17:27 +02:00
Jan Christian Grünhage 52257759cb feat: reply to commands
Reply to commands in addition to executing them.
2020-10-16 00:13:16 +02:00
Jan Christian Grünhage 3eb8ae7ddb docs: add license, readme and changelog 2020-10-16 00:12:51 +02:00
5 changed files with 135 additions and 47 deletions

View file

@ -11,9 +11,30 @@ The format is based on [Keep a Changelog], and this project adheres to
## Overview
<!-- [releases] -->
<!-- [commits] -->
- [`0.1.0`](#010) _2020.10.15_## [0.1.0] _Initial release_
_2020.10.15_
This is the first release. It contains basic waking up of hosts,
autojoining, permission checks and status replies.
### Contributions
This release is made possible by the following people (in alphabetical order).
Thank you all for your contributions. Your work no matter how significant is
greatly appreciated by the community. 💖
- Jan Christian Grünhage (<jan.christian@gruenhage.xyz>)
### Changes
#### Features
- **reply to commands** ([`5225775`])
Reply to commands in addition to executing them.
@ -85,18 +106,5 @@ greatly appreciated by the community. 💖
{% endfor %}
{%- endfor -%}
<!-- [releases] -->
{%- for release in releases %}
[{{ release.version }}]: {{ url }}/releases/tag/v{{ release.version }}
{%- endfor %}
<!-- [commits] -->
{%- for release in releases %}
{%- for change in release.changeset.changes %}
[`{{ change.commit.short_id }}`]: {{ url }}/commit/{{ change.commit.id }}
{%- endfor -%}
{%- endfor %}
)
-->

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>,
}