diff --git a/src/lib.rs b/src/lib.rs index fc8941b..b633ac1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -198,6 +198,14 @@ pub trait MessageSender : Sync + Send + std::fmt::Debug { fn send_message (&self, _: &str) {} } +pub struct NullMessageSender {} +impl MessageSender for NullMessageSender {} +impl std::fmt::Debug for NullMessageSender { + fn fmt (&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "NullMessageSender") + } +} + struct Subscription { pub name: String, pub filters: Vec> diff --git a/src/modules/lua.rs b/src/modules/lua.rs index aeea54a..9766128 100644 --- a/src/modules/lua.rs +++ b/src/modules/lua.rs @@ -4,10 +4,10 @@ use toml::Value; use toml::value::Table; use hlua; -use hlua::{Lua, LuaFunction, Push}; +use hlua::{Lua, LuaFunction, AnyHashableLuaValue, AnyLuaValue}; +use {User, Message, Channel, NullMessageSender}; use event::{Event, Envelope}; -use Message; use std::sync::Arc; use std::sync::mpsc::Receiver; @@ -16,6 +16,8 @@ use transformable_channels::mpsc::ExtSender; use std::path::Path; use std::fs::File; +use std::collections::HashMap; + pub struct LuaModule { code: Option, file: Option, @@ -42,15 +44,76 @@ fn set (lua: &mut Lua, key: &str, value: &Value) { } } -implement_lua_read!(Message); -implement_lua_push!(Message, |mut metatable| { +struct MessageWrapper { + envelope: Arc +} + +implement_lua_read!(MessageWrapper); +implement_lua_push!(MessageWrapper, |mut metatable| { let mut index = metatable.empty_array("__index"); - index.set("content", hlua::function1(|message: &mut Message| message.content.clone())); - index.set("reply", hlua::function2(|message: &mut Message, reply: String| message.reply(&reply))); + index.set("content", hlua::function1(|wrapper: &MessageWrapper| { + if let Event::Message { ref message } = wrapper.envelope.event { + Some(message.content.clone()) + } else { + None + } + })); + index.set("reply", hlua::function2(|wrapper: &MessageWrapper, reply: String| { + if let Event::Message { ref message } = wrapper.envelope.event { + message.reply(&reply); + } + })); +}); + +struct SenderWrapper { + sender: Box> +} + +implement_lua_read!(SenderWrapper); +implement_lua_push!(SenderWrapper, |mut metatable| { + let mut index = metatable.empty_array("__index"); + index.set("send", hlua::function2(|wrapper: &SenderWrapper, data: HashMap| { + if let Some(&AnyLuaValue::LuaString(ref event_type)) = data.get(&AnyHashableLuaValue::LuaString("type".to_owned())) { + match event_type.as_ref() { + "message" => { + wrapper.sender.send(Event::Message { + message: Message { + author: User { + name: data.get(&AnyHashableLuaValue::LuaString("username".to_owned())) + .and_then(|value| match value { + &AnyLuaValue::LuaString (ref string_value) => Some(string_value.to_owned()), + _ => None + }) + .unwrap_or_else(|| "".to_owned()), + sender: Box::new(NullMessageSender {}) + }, + channel: data.get(&AnyHashableLuaValue::LuaString("channel".to_owned())) + .and_then(|value| match value { + &AnyLuaValue::LuaString (ref string_value) => Some(string_value.to_owned()), + _ => None + }).map(|channel| Channel { + name: channel, + description: "".to_owned(), + sender: Box::new(NullMessageSender {}), + topic: "".to_owned() + }), + content: data.get(&AnyHashableLuaValue::LuaString("message".to_owned())) + .and_then(|value| match value { + &AnyLuaValue::LuaString (ref string_value) => Some(string_value.to_owned()), + _ => None + }) + .expect("Invalid message event passed in") + } + }); + }, + _ => {} + } + } + })); }); impl EventLoop for LuaModule { - fn run (&self, _: Box>, receiver: Receiver>) { + fn run (&self, sender: Box>, receiver: Receiver>) { let mut lua = Lua::new(); lua.openlibs(); @@ -58,6 +121,10 @@ impl EventLoop for LuaModule { set(&mut lua, key, value); } + lua.set("sender", SenderWrapper { + sender: Box::new(sender.clone()) + }); + match self.code { Some(ref code) => { lua.execute(&code).unwrap() }, None => { @@ -81,23 +148,9 @@ impl EventLoop for LuaModule { let on_message: Option> = lua.get("on_message"); match on_message { Some(mut on_message) => { - /* - * What this does is clone the envelope reference (an Arc - atomic refcounted) - * and pass it into the reply_callback, where it becomes owned by the closure - * (since it's a move closure). We then pass this callback into the lua code, - * where it can be used to send a reply. - * - * TODO: Replace this with a table (struct/class) which has a - * reply() method. - */ - let envelope_clone = envelope.clone(); - let reply_callback = hlua::function1(move |reply: String| { - match envelope_clone.event { - Event::Message { ref message } => message.reply(&reply), - _ => {} - } - }); - on_message.call_with_args::<(), _, _>((message.content.clone(), reply_callback)).unwrap(); + on_message.call_with_args::<(), _, _>(MessageWrapper { + envelope: envelope.clone() + }).unwrap(); }, None => {} } diff --git a/tenquestionmarks.toml b/tenquestionmarks.toml index 0068fdd..40d52f1 100644 --- a/tenquestionmarks.toml +++ b/tenquestionmarks.toml @@ -78,8 +78,8 @@ parents = ["stdin", "discord", "irc"] [lua] parents = ["stdin", "discord"] code = """ -function on_message (message, reply) - reply("Lua says: " .. message) +function on_message (message) + message:reply("Lua says: " .. message:content()) end """ foo = "bar" @@ -89,8 +89,24 @@ type = "lua" parents = ["stdin", "discord"] filters = [{ username = "David" }] code = """ -function on_message (message, reply) - reply("Lua2 says: " .. message) +function on_message (message) + message:reply("Lua2 says: " .. message:content()) +end +""" + +[lua3] +type = "lua" +children = ["lua", "irc"] +code = """ +local clock = os.clock +function sleep(n) -- seconds + local t0 = clock() + while clock() - t0 <= n do end +end + +while true do + sender:send({type = "message", channel = "#eightbar", message = "Hello world!"}) + sleep(10) end """