...
 
Commits (3)
......@@ -78,17 +78,18 @@ impl BoardBot {
fn list(&self, response: &Response) -> Result<(), CmdError> {
let state = self.state.read();
if !state.is_empty() {
response.reply(&format!("The following ballots are currently open:"))?;
response.respond_message(&format!("The following ballots are currently open:"))?;
for (id, poll) in state.iter() {
response.reply(&format!(" <{}> {}: {} (created by {} at {})",
id,
poll.text(),
poll.opinions(),
poll.creator(),
poll.creation_date()))?;
response.respond_message(&format!(" <{}> {} [{}] (created by {} at {})",
id,
poll.text(),
poll.opinions(),
poll.creator(),
poll.creation_date()))?;
}
response.respond_message("--")?;
} else {
response.reply(&format!("Currently, there are not open ballots."))?;
response.respond_message(&format!("Currently, there are not open ballots."))?;
}
return Ok(());
......@@ -106,14 +107,14 @@ impl BoardBot {
})?;
}
response.reply(&format!("Your opinion for ballot <{}> has been recorded", id))?;
response.respond_message(&format!("Your opinion for ballot <{}> has been recorded", id))?;
// Auto-commit if everybody has voted
if ballot.opinions().count() == self.board_members.len() {
self.finish(response, ballot.delete())?;
}
} else {
response.reply(&format!("No such ballot: <{}>", id))?;
response.respond_message(&format!("No such ballot: <{}>", id))?;
}
return Ok(());
......@@ -126,10 +127,10 @@ impl BoardBot {
if ballot.opinion_count() > self.board_members.len() / 2 {
self.finish(response, ballot.delete())?;
} else {
response.reply(&format!("Not enough votes to commit: <{}> ({})", id, ballot.opinions()))?;
response.respond_message(&format!("Not enough votes to commit: <{}> ({})", id, ballot.opinions()))?;
}
} else {
response.reply(&format!("No such ballot: <{}>", id))?;
response.respond_message(&format!("No such ballot: <{}>", id))?;
};
return Ok(());
......@@ -141,10 +142,10 @@ impl BoardBot {
if let Some(ballot) = state.get(id) {
let ballot = ballot.delete();
self.announce(response, &ballot,
&format!("{} has canceled this ballot: {}: {} (created by {} at {})",
&format!("{} has canceled this ballot: {}: [{}] (created by {} at {})",
nick, ballot.text(), ballot.opinions(), ballot.creator(), ballot.creation_date()))?;
} else {
response.reply(&format!("Unknown ballot: <{}>", id))?;
response.respond_message(&format!("Unknown ballot: <{}>", id))?;
}
return Ok(());
......@@ -160,12 +161,12 @@ impl BoardBot {
match pattern {
Ok(pattern) => {
for line in self.repository.search(&pattern) {
response.reply(&line)?;
response.respond_message(&line)?;
}
}
Err(err) => {
response.reply(&format!("Invalid expression: {}", err))?;
response.respond_message(&format!("Invalid expression: {}", err))?;
}
}
......@@ -177,12 +178,12 @@ impl BoardBot {
self.repository.append(&resolution);
self.announce(response, &ballot,
&format!("Resolution {}: {} (at {} with {})",
resolution.conclusion(), resolution.text(), resolution.date(), resolution.opinions()))?;
&format!("Resolution {}: {} [{}]",
resolution.conclusion(), resolution.text(), resolution.opinions()))?;
response.send(&ballot.creator(),
&format!("Your proposed resolution: {} has been {}: {}",
resolution.text(), resolution.conclusion(), resolution.opinions()))?;
response.message(&ballot.creator(),
&format!("Your proposed resolution: {} has been {} [{}]",
resolution.text(), resolution.conclusion(), resolution.opinions()))?;
return Ok(());
}
......@@ -193,7 +194,7 @@ impl BoardBot {
if self.board_members.iter().any(|s| s == nick) {
return f().map_err(|err| err.into());
} else {
response.reply(&format!("Sorry, only members of the board can do that."))?;
response.respond_message(&format!("Sorry, only members of the board can do that."))?;
}
return Ok(());
......@@ -207,11 +208,11 @@ impl BoardBot {
}
for member in self.board_members.iter() {
response.notice(&member, &message)?;
response.message(&member, &message)?;
}
if !self.board_members.contains(ballot.creator()) {
response.notice(&ballot.creator(), &message)?;
response.message(&ballot.creator(), &message)?;
}
return Ok(());
......
......@@ -9,6 +9,7 @@ use std::io::BufReader;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::process::Command;
use log::debug;
pub struct Repository(PathBuf);
......@@ -58,7 +59,7 @@ impl Repository {
let path = self.0.join("resolutions").join(format!("{}", resolution.date().year()));
println!("Appending to resolution file: {:?}", path);
debug!("Appending to resolution file: {:?}", path);
let mut file = OpenOptions::new()
.create(true)
......
......@@ -78,8 +78,8 @@ impl PizzaBot {
*state = Some(run);
response.send(&self.channel,
&format!("New order started. Deadline for this order is {:02}:{:02} and {} is in charge place the order",
response.message(&self.channel,
&format!("New order started. Deadline for this order is {:02}:{:02} and {} is in charge place the order",
deadline.hour(),
deadline.minute(),
nick))?;
......@@ -88,9 +88,9 @@ impl PizzaBot {
let run = state.as_mut().unwrap();
if let Some(old) = run.orders.insert(nick.to_string(), order.to_string()) {
response.reply(&format!("Your item change has been recorded (was: {})", old))?;
response.respond_message(&format!("Your item change has been recorded (was: {})", old))?;
} else {
response.reply(&format!("Your item has been recorded."))?;
response.respond_message(&format!("Your item has been recorded."))?;
}
return Ok(());
......@@ -102,7 +102,7 @@ impl PizzaBot {
if state.is_some() {
return f(&mut state).map_err(|err| err.into());
} else {
response.reply("No active order")?;
response.respond_message("No active order")?;
}
return Ok(());
......@@ -116,9 +116,9 @@ impl PizzaBot {
fn purge(&mut self, response: &Response, nick: &str) -> Result<(), CmdError> {
return self.ensure_run(response, |run| {
if let Some(old) = run.orders.remove(nick) {
response.reply(&format!("Your item has been removed from the order (was: {})", old))?;
response.respond_message(&format!("Your item has been removed from the order (was: {})", old))?;
} else {
response.reply(&format!("Error: You haven't placed an items in this order yet"))?;
response.respond_message(&format!("Error: You haven't placed an items in this order yet"))?;
}
return Ok(());
......@@ -135,27 +135,27 @@ impl PizzaBot {
chrono::format::Item::Literal(":"),
chrono::format::Item::Numeric(chrono::format::Numeric::Minute, chrono::format::Pad::Zero),
].iter().cloned()) {
response.reply(&format!("Error: Deadline format must be HH:MM"))?;
response.respond_message(&format!("Error: Deadline format must be HH:MM"))?;
return Ok(()); // FIXME: Command-Error handling
}
let deadline = Local::today().and_time(parsed.to_naive_time().unwrap()).unwrap();
if deadline <= Local::now() {
response.reply(&format!("Error: Deadline must be in the future"))?;
response.respond_message(&format!("Error: Deadline must be in the future"))?;
return Ok(()); // FIXME: Command-Error handling
}
run.deadline = deadline;
response.send(&self.channel,
&format!("Deadline for this order has been changed {:02}:{:02}",
response.message(&self.channel,
&format!("Deadline for this order has been changed {:02}:{:02}",
run.deadline.hour(),
run.deadline.minute()))?;
} else {
// Show current deadline
response.reply(&format!("Deadline for this order is {:02}:{:02}",
run.deadline.hour(),
run.deadline.minute()))?;
response.respond_message(&format!("Deadline for this order is {:02}:{:02}",
run.deadline.hour(),
run.deadline.minute()))?;
}
return Ok(());
......@@ -167,9 +167,9 @@ impl PizzaBot {
if let Some(_) = *run {
if ack {
run.take();
response.reply(&format!("The order have been canceled and all items have been purged"))?;
response.respond_message(&format!("The order have been canceled and all items have been purged"))?;
} else {
response.reply(&format!("This will remove ALL items from all users and drop the deadline.\nIf you're sure you want to do that, user 'cancel --acknowledge'"))?;
response.respond_message(&format!("This will remove ALL items from all users and drop the deadline.\nIf you're sure you want to do that, user 'cancel --acknowledge'"))?;
}
}
......@@ -179,15 +179,15 @@ impl PizzaBot {
fn show(&self, response: &Response) -> Result<(), CmdError> {
return self.ensure_run(response, |run| {
response.reply(&format!("The following items have been recorded:"))?;
response.respond_message(&format!("The following items have been recorded:"))?;
for (k, v) in run.orders.iter() {
response.reply(&format!(" {}: {}", k, v))?;
response.respond_message(&format!(" {}: {}", k, v))?;
}
response.reply(&format!("-- Deadline for this order is {:02}:{:02}",
run.deadline.hour(),
run.deadline.minute()))?;
response.respond_message(&format!("-- Deadline for this order is {:02}:{:02}",
run.deadline.hour(),
run.deadline.minute()))?;
return Ok(());
});
......@@ -212,19 +212,19 @@ impl CommandModule for PizzaBot {
if due {
let run = state.take().unwrap();
client.send(&channel,
&format!("Ok, time is over. {}, you have to place the order:", run.owner))?;
client.message(&channel,
&format!("Ok, time is over. {}, you have to place the order:", run.owner))?;
client.send(&run.owner,
&format!("It's your turn. Please place the following order:"))?;
client.message(&run.owner,
&format!("It's your turn. Please place the following order:"))?;
for (k, v) in run.orders.iter() {
client.send(&run.owner,
&format!(" {}: {}", k, v))?;
client.message(&run.owner,
&format!(" {}: {}", k, v))?;
}
client.send(&run.owner,
"--")?;
client.message(&run.owner,
"--")?;
}
return Ok(());
......
......@@ -3,10 +3,14 @@ use crate::bot::Bot;
use crate::bot::Module;
use crate::config;
use serde::Deserialize;
use simple_logger;
use log;
pub fn bootstrap<M>(name: &str)
where M: Module + 'static,
for<'de> <M as Module>::Config: Deserialize<'de> {
simple_logger::init().unwrap();
let binary = format!("The hackerspace management IRC bot - {} module", name);
let config = format!("/etc/spacebot/{}.yaml", name);
......
use crate::config::ModuleConfig;
use failure::{Fail,Error};
use failure::Error;
use irc::client::Client as ClientX;
use irc::client::PackedIrcClient;
use irc::client::prelude::*;
use irc::error::IrcError;
use log::{debug, info, trace};
use tokio::prelude::{future, IntoFuture};
use tokio::runtime::current_thread::Runtime;
use log::{trace, debug, info, warn, error};
use crate::config::ModuleConfig;
pub struct Bot<M>
where M: Module {
......@@ -27,10 +27,10 @@ pub struct Request<'a> {
#[derive(Clone)]
pub struct Response<'a> {
connection: &'a IrcClient,
pub client: &'a Client,
/// Target used for responses to this message
response_target: &'a str,
pub respondee: &'a str,
}
pub struct Initializer {
......@@ -78,38 +78,21 @@ impl<'a> Request<'a> {
}
impl<'a> Response<'a> {
pub fn reply(&self, message: &str) -> Result<(), Error> {
return self.send(self.response_target, message);
pub fn respond_message(&self, message: &str) -> Result<(), Error> {
return self.message(self.respondee, message);
}
pub fn response_target(&self) -> &'a str {
return self.response_target;
}
pub fn send(&self, target: &str, message: &str) -> Result<(), Error> {
for line in message.split("\n") {
if line != "" {
self.connection.send_privmsg(target, line)?;
}
}
return Ok(());
pub fn message(&self, target: &str, message: &str) -> Result<(), Error> {
return self.client.message(target, message);
}
pub fn notice(&self, target: &str, message: &str) -> Result<(), Error> {
for line in message.split("\n") {
if line != "" {
self.connection.send_notice(target, line)?;
}
}
return Ok(());
return self.client.notice(target, message);
}
}
impl Client {
// FIXME: Get rid of duplicated code: Client vs Response and line splitting
pub fn send(&self, target: &str, message: &str) -> Result<(), Error> {
pub fn message(&self, target: &str, message: &str) -> Result<(), Error> {
for line in message.split("\n") {
if line != "" {
self.connection.send_privmsg(target, line)?;
......@@ -175,22 +158,22 @@ impl<M> Bot<M>
// Handle incoming messages
let messages_future = connection.stream().from_err().for_each(move |message| {
trace!("{}", message);
if let Command::PRIVMSG(ref target, ref text) = message.command {
let source = if let Some(source) = message.source_nickname() { source } else { return Ok(()) };
let respondee = if target.is_channel_name() { target } else { source };
let request = Request {
message: text,
source: message.source_nickname().expect("No source nick found"), // TODO: Ignore such messages
source,
target,
};
let response = Response {
connection: &connection,
response_target: message.response_target().expect("Unknown sender"), // TODO: Ignore such messages
client: &Client { connection: connection.clone() },
respondee,
};
return module.handle(request, response);
} else {
return Ok(());
}
......
......@@ -91,7 +91,7 @@ impl<M> Module for M
match result {
Err(Error::CommandError {msg}) => {
response.reply(&format!("Error: {}", &msg))?;
response.respond_message(&format!("Error: {}", &msg))?;
}
Err(Error::BotError(err)) => return Err(err),
Ok(result) => return Ok(result),
......@@ -101,12 +101,12 @@ impl<M> Module for M
Err(err) => {
match err.kind {
// TODO: Show help only for query - send a one-line answer to channels
AppErrorKind::HelpDisplayed => response.send(
AppErrorKind::HelpDisplayed => response.message(
request.source(),
&err.message
)?,
_ => response.reply(&format!("Error: {}", &err.message))?,
_ => response.respond_message(&format!("Error: {}", &err.message))?,
};
}
};
......