beefed up lua scripting support, the message is now presented as a table/struct/object and lua now has access to a sender object which it can use to send messages downstream.

This commit is contained in:
Adrian Malacoda 2018-02-25 07:04:47 -06:00
parent c6dc3f15b8
commit b0c2928e78
3 changed files with 105 additions and 28 deletions

View File

@ -198,6 +198,14 @@ pub trait MessageSender : Sync + Send + std::fmt::Debug {
fn send_message (&self, _: &str) {} 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 { struct Subscription {
pub name: String, pub name: String,
pub filters: Vec<Box<EventFilter>> pub filters: Vec<Box<EventFilter>>

View File

@ -4,10 +4,10 @@ use toml::Value;
use toml::value::Table; use toml::value::Table;
use hlua; use hlua;
use hlua::{Lua, LuaFunction, Push}; use hlua::{Lua, LuaFunction, AnyHashableLuaValue, AnyLuaValue};
use {User, Message, Channel, NullMessageSender};
use event::{Event, Envelope}; use event::{Event, Envelope};
use Message;
use std::sync::Arc; use std::sync::Arc;
use std::sync::mpsc::Receiver; use std::sync::mpsc::Receiver;
@ -16,6 +16,8 @@ use transformable_channels::mpsc::ExtSender;
use std::path::Path; use std::path::Path;
use std::fs::File; use std::fs::File;
use std::collections::HashMap;
pub struct LuaModule { pub struct LuaModule {
code: Option<String>, code: Option<String>,
file: Option<String>, file: Option<String>,
@ -42,15 +44,76 @@ fn set (lua: &mut Lua, key: &str, value: &Value) {
} }
} }
implement_lua_read!(Message); struct MessageWrapper {
implement_lua_push!(Message, |mut metatable| { envelope: Arc<Envelope>
}
implement_lua_read!(MessageWrapper);
implement_lua_push!(MessageWrapper, |mut metatable| {
let mut index = metatable.empty_array("__index"); let mut index = metatable.empty_array("__index");
index.set("content", hlua::function1(|message: &mut Message| message.content.clone())); index.set("content", hlua::function1(|wrapper: &MessageWrapper| {
index.set("reply", hlua::function2(|message: &mut Message, reply: String| message.reply(&reply))); 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<ExtSender<Event>>
}
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<AnyHashableLuaValue, AnyLuaValue>| {
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 { impl EventLoop for LuaModule {
fn run (&self, _: Box<ExtSender<Event>>, receiver: Receiver<Arc<Envelope>>) { fn run (&self, sender: Box<ExtSender<Event>>, receiver: Receiver<Arc<Envelope>>) {
let mut lua = Lua::new(); let mut lua = Lua::new();
lua.openlibs(); lua.openlibs();
@ -58,6 +121,10 @@ impl EventLoop for LuaModule {
set(&mut lua, key, value); set(&mut lua, key, value);
} }
lua.set("sender", SenderWrapper {
sender: Box::new(sender.clone())
});
match self.code { match self.code {
Some(ref code) => { lua.execute(&code).unwrap() }, Some(ref code) => { lua.execute(&code).unwrap() },
None => { None => {
@ -81,23 +148,9 @@ impl EventLoop for LuaModule {
let on_message: Option<LuaFunction<_>> = lua.get("on_message"); let on_message: Option<LuaFunction<_>> = lua.get("on_message");
match on_message { match on_message {
Some(mut on_message) => { Some(mut on_message) => {
/* on_message.call_with_args::<(), _, _>(MessageWrapper {
* What this does is clone the envelope reference (an Arc - atomic refcounted) envelope: envelope.clone()
* and pass it into the reply_callback, where it becomes owned by the closure }).unwrap();
* (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();
}, },
None => {} None => {}
} }

View File

@ -78,8 +78,8 @@ parents = ["stdin", "discord", "irc"]
[lua] [lua]
parents = ["stdin", "discord"] parents = ["stdin", "discord"]
code = """ code = """
function on_message (message, reply) function on_message (message)
reply("Lua says: " .. message) message:reply("Lua says: " .. message:content())
end end
""" """
foo = "bar" foo = "bar"
@ -89,8 +89,24 @@ type = "lua"
parents = ["stdin", "discord"] parents = ["stdin", "discord"]
filters = [{ username = "David" }] filters = [{ username = "David" }]
code = """ code = """
function on_message (message, reply) function on_message (message)
reply("Lua2 says: " .. 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 end
""" """