From 12024c66fe21c62b158552abe26b1bcfa142ffe8 Mon Sep 17 00:00:00 2001
From: Kevin Smith <git@kismith.co.uk>
Date: Sat, 21 Jul 2012 19:15:37 +0100
Subject: Allowing generic message listeners for Swiftob bots


diff --git a/Swiftob/Commands.cpp b/Swiftob/Commands.cpp
index cf24196..18f9fb0 100644
--- a/Swiftob/Commands.cpp
+++ b/Swiftob/Commands.cpp
@@ -41,6 +41,10 @@ void Commands::registerCommand(const std::string& name, RoleList roles, const st
 	command->onReceived.connect(callback);
 }
 
+void Commands::registerListener(ListenerCallback listener) {
+	listeners_.push_back(listener);
+}
+
 bool Commands::hasCommand(const std::string& name) {
 	return commands_.find(name) != commands_.end();
 }
@@ -57,6 +61,12 @@ bool Commands::runCommand(const std::string& name, const std::string& params, Sw
 	return false;
 }
 
+void Commands::runListeners(Swift::Message::ref message) {
+	foreach (ListenerCallback listener, listeners_) {
+		listener(message);
+	}
+}
+
 bool Commands::roleIn(const Users::User::Role userRole, RoleList roleList) {
 	switch (roleList) {
 		case Owner : return userRole == Users::User::Owner;
@@ -84,6 +94,7 @@ void Commands::handleRehashCommand(const std::string& /*command*/, const std::st
 	replyTo(message, "Rehashing now.");
 	std::cout << "Rehashing at the behest of " << message->getFrom().toString() << std::endl;
 	resetCommands();
+	listeners_.clear();
 	if (rehashError_.empty()) {
 		replyTo(message, "Rehash complete");
 	} else {
diff --git a/Swiftob/Commands.h b/Swiftob/Commands.h
index 8423252..5c55f39 100644
--- a/Swiftob/Commands.h
+++ b/Swiftob/Commands.h
@@ -21,6 +21,7 @@ namespace Swift {
 
 class Storage;
 class Commands {
+	typedef boost::function<void(Swift::Message::ref)> ListenerCallback;
 	public:
 		enum RoleList {Anyone, Owner};
 	public:
@@ -43,8 +44,10 @@ class Commands {
 		Commands(Users* users, Swift::Client* client, Storage* storage, MUCs* mucs);
 		bool hasCommand(const std::string&);
 		bool runCommand(const std::string& command, const std::string& params, Swift::Message::ref message);
+		void runListeners(Swift::Message::ref message);
 		void replyTo(Swift::Message::ref source, std::string replyBody, bool outOfMUC = false);
 		void registerCommand(const std::string& name, RoleList roles, const std::string& description, boost::function<void(const std::string& /*command*/, const std::string& /*params*/, Swift::Message::ref)> callback);
+		void registerListener(ListenerCallback);
 		void resetCommands();
 		void setRehashError(const std::string& error);
 
@@ -61,6 +64,7 @@ class Commands {
 		void handleRehashCommand(const std::string& command, const std::string& params, Swift::Message::ref message);
 	private:
 		std::map<std::string, Command*> commands_;
+		std::vector<ListenerCallback> listeners_;
 		Users* users_;
 		Swift::Client* client_;
 		Storage* storage_;
diff --git a/Swiftob/LuaCommands.cpp b/Swiftob/LuaCommands.cpp
index 9f99d82..8cc614d 100644
--- a/Swiftob/LuaCommands.cpp
+++ b/Swiftob/LuaCommands.cpp
@@ -42,6 +42,21 @@ void LuaCommands::registerCommands() {
 	}
 }
 
+static int l_register_listener(lua_State *L) {
+	LuaCommands* commands = NULL;
+	lua_getfield(L, LUA_REGISTRYINDEX, LUA_COMMANDS);
+	commands = static_cast<LuaCommands*>(lua_touserdata(L, -1));
+	lua_pop(L, 1);
+	if (!lua_isfunction(L, 1)) {
+		return luaL_error(L, "register_listener parameter must be a callback function");
+	}
+	lua_pushvalue(L, 1);
+	int callbackIndex = luaL_ref(L, LUA_REGISTRYINDEX);
+	lua_pop(L, 1);
+	commands->getCommands()->registerListener(boost::bind(&LuaCommands::handleLuaListener, commands, callbackIndex, L, _1));
+	return 0;
+}
+
 static int l_register_command(lua_State *L) {
 	LuaCommands* commands = NULL;
 	lua_getfield(L, LUA_REGISTRYINDEX, LUA_COMMANDS);
@@ -313,6 +328,21 @@ int LuaCommands::get_setting(lua_State *L) {
 
 }
 
+void LuaCommands::handleLuaListener(int callbackIndex, lua_State* L, Swift::Message::ref message) {
+	lua_rawgeti(L, LUA_REGISTRYINDEX, callbackIndex);
+	lua_pushstring(L, message->getBody().c_str());
+	lua_pushstring(L, message->getFrom().toBare().toString().c_str());
+	lua_pushstring(L, message->getFrom().getResource().c_str());
+	messageOntoStack(message, L);
+	int result = lua_pcall(L, 4, 0, 0);
+	if (result != 0) {
+		std::string error(lua_tostring(L, -1));
+		lua_pop(L, 1);
+		error = "Listener failed: " + error;
+		std::cout << error << std::endl;
+	}
+}
+
 void LuaCommands::handleLuaCommand(int callbackIndex, lua_State* L, const std::string& command, const std::string& params, Swift::Message::ref message) {
 	lua_rawgeti(L, LUA_REGISTRYINDEX, callbackIndex);
 	lua_pushstring(L, command.c_str());
@@ -363,6 +393,7 @@ void LuaCommands::loadScript(boost::filesystem::path filePath) {
 	lua_pushlightuserdata(lua, storage);
 	lua_setfield(lua, LUA_REGISTRYINDEX, STORAGE);
 	lua_register(lua, "swiftob_register_command", &l_register_command);
+	lua_register(lua, "swiftob_register_listener", &l_register_listener);
 	lua_register(lua, "swiftob_reply_to", &l_reply_to);
 	lua_register(lua, "swiftob_get_software_version", &l_get_software_version);
 	lua_register(lua, "swiftob_muc_input_to_jid", &l_muc_input_to_jid);
diff --git a/Swiftob/LuaCommands.h b/Swiftob/LuaCommands.h
index fc743ca..b82959e 100644
--- a/Swiftob/LuaCommands.h
+++ b/Swiftob/LuaCommands.h
@@ -64,8 +64,9 @@ class LuaCommands {
 		};
 
 		LuaCommands(Commands* commands, const std::string& path, Client* client, TimerFactory* timerFactory, MUCs* mucs);
-		/* Public but isn't really part of the API */
+		/* Public but aren't really part of the API */
 		void handleLuaCommand(int callbackIndex, lua_State* L, const std::string& command, const std::string& params, Message::ref message);
+		void handleLuaListener(int callbackIndex, lua_State* L, Message::ref message);
 		Commands* getCommands() {return commands_;}
 		int get_software_version(lua_State *L);
 		int muc_input_to_jid(lua_State *L);
diff --git a/Swiftob/Swiftob.cpp b/Swiftob/Swiftob.cpp
index 331e55e..6f36b3d 100644
--- a/Swiftob/Swiftob.cpp
+++ b/Swiftob/Swiftob.cpp
@@ -87,6 +87,10 @@ void Swiftob::handleMessageReceived(Swift::Message::ref message) {
 		std::cout << "Not handling empty body" << std::endl;
 		return;
 	}
+
+	/* Run through any full-message listeners */
+	commands_->runListeners(message);
+
 	/*Convert body into !command if it's not a MUC, and it misses the bang*/
 	std::string bangBody(body);
 	if (type != Swift::Message::Groupchat && body[0] != '!') {
diff --git a/Swiftob/scripts/logAllMessages.lua b/Swiftob/scripts/logAllMessages.lua
new file mode 100644
index 0000000..a14c0f3
--- /dev/null
+++ b/Swiftob/scripts/logAllMessages.lua
@@ -0,0 +1,6 @@
+function log_a_message(body, muc, nick, message)
+   print("Received line from '" .. nick .. "' in '" .. muc .. "':")
+   print(body)
+end
+
+swiftob_register_listener(log_a_message)
-- 
cgit v0.10.2-6-g49f6