Initial commit, hello world!

Mon, 21 Dec 2009 19:27:08 +0000

author
Matthew Wild <mwild1@gmail.com>
date
Mon, 21 Dec 2009 19:27:08 +0000
changeset 0
7d84f4403d67
child 1
f9e4ee00f557

Initial commit, hello world!

init.lua file | annotate | diff | comparison | revisions
plugins/commands.lua file | annotate | diff | comparison | revisions
plugins/groupchat.lua file | annotate | diff | comparison | revisions
plugins/ping.lua file | annotate | diff | comparison | revisions
plugins/version.lua file | annotate | diff | comparison | revisions
squishy file | annotate | diff | comparison | revisions
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/init.lua	Mon Dec 21 19:27:08 2009 +0000
@@ -0,0 +1,144 @@
+local verse = require "verse";
+local st = require "util.stanza";
+
+module("riddim", package.seeall);
+plugins = {};
+
+local riddim_mt = {};
+riddim_mt.__index = riddim_mt;
+
+function new(stream, config)
+	if not stream then
+		error("riddim.new(): Verse stream required as first parameter", 2);
+	end
+	return setmetatable({ stream = stream, config = config or {} }, riddim_mt);
+end
+
+-- self.conn is ready for stanzas
+function riddim_mt:start()
+	self:add_plugin("groupchat");
+	self:add_plugin("commands");
+	self:add_plugin("ping");
+	self:event("started");
+	self.stream:hook("stanza", function (stanza)
+			local body = stanza:get_child("body");
+			local event = {
+				sender = { jid = stanza.attr.from };
+				body = (body and body:get_text()) or nil;
+				stanza = stanza;
+			};
+			if stanza.name == "message" then
+				local replied;
+				local bot = self;
+				function event:reply(reply)
+					if replied then return false; end
+					replied = true;
+					return bot:send_message(stanza.attr.from, reply);
+				end
+			end
+			local ret;
+			if stanza.name == "iq" and (stanza.attr.type == "get" or stanza.attr.type == "set") then
+				local xmlns = stanza.tags[1] and stanza.tags[1].attr.xmlns;
+				if xmlns then
+					event.xmlns = xmlns;
+					print(event.stanza)
+					ret = self:event("iq/"..xmlns, event);
+					if not ret then
+						ret = self:event(stanza.name, event);
+					end
+				end
+			else
+				ret = self:event(stanza.name, event);
+			end
+			
+			if ret and type(ret) == "table" and ret.name then
+				self:send(ret);
+			end
+			return ret;
+		end, 1);
+end
+
+function riddim_mt:send(s)
+	return self.stream:send(tostring(s));
+end
+
+function riddim_mt:event(name, ...)
+	return self.stream:event("bot/"..name, ...);
+end
+	
+function riddim_mt:hook(name, ...)
+	return self.stream:hook("bot/"..name, ...);
+end
+
+function riddim_mt:send_message(to, text, formatted_text)
+	self:send(st.message({ to = to, type = "chat" }):tag("body"):text(text));
+end
+
+function riddim_mt:add_plugin(name)
+	require("riddim.plugins."..name);
+	return riddim.plugins[name](self);
+end
+	
+-- Built-in bot starter
+if not (... and package.loaded[...] ~= nil) then
+	require "verse.client";
+	
+	-- Config loading
+	local chunk, err = loadfile("config.lua");
+	if not chunk then
+		print("File or syntax error:", err);
+		return 1;
+	end
+
+	local config = {};
+	setfenv(chunk, setmetatable(config, {__index = _G}));
+	local ok, err = pcall(chunk);
+	if not ok then
+		print("Error while processing config:", err);
+		return 1;
+	end
+	setmetatable(config, nil);
+
+	if not config.jid then
+		io.write("Enter the bot's JID: ");
+		config.jid = io.read("*l");
+	end
+	
+	if not config.password then
+		io.write("Enter the password for "..config.jid..": ");
+		config.password = io.read("*l");
+	end
+	
+	-- Create the stream object and bot object
+	local c = verse.new();
+	local b = riddim.new(c, config);
+	
+	if config.debug then
+		c:hook("incoming-raw", print);
+	end
+	
+	for _, plugin in ipairs(config.plugins or {"ping"}) do
+		c:add_plugin(plugin);
+	end
+	
+	b:hook("started", function ()
+		b:send(verse.presence());
+		for k, v in pairs(autojoin or {}) do
+			if type(k) == "number" then
+				b:join_room(v);
+			elseif type(k) == "string" then
+				if type(v) == "string" then
+					b:join_room(k, v);
+				end
+			end
+		end
+	end);
+	
+	c:hook("binding-success", function () b:start(); end)
+	
+	c:connect_client(config.jid, config.password);
+	
+	verse.loop();
+end
+
+return _M;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/commands.lua	Mon Dec 21 19:27:08 2009 +0000
@@ -0,0 +1,46 @@
+require "util.xstanza"
+
+function riddim.plugins.commands(bot)
+	local function handle_message(message)
+		-- Parse message body
+	end
+	
+	local command_pattern = "^%"..(bot.config.command_prefix or "@").."([%a%-%_%d]+)(%s?)(.*)$";
+
+	local function process_command(event)
+		local body, sender = event.body, event.sender;
+		if not body then return; end
+		if event.delay then return; end -- Don't process old messages
+
+		local command, hasparam, param = body:match(command_pattern);
+	
+		if not command then
+			command, hasparam, param = body:match("%[([%a%-%_%d]+)(%s?)(.*)%]");
+		end
+		
+		if hasparam ~= " " then param = nil; end
+	
+		if command then
+			local command_event = {
+						command = command,
+						param = param,
+						sender = sender,
+						stanza = event.stanza,
+						reply = event.reply,
+						room = event.room,
+					};
+			local ret = bot:event("commands/"..command, command_event);
+			if type(ret) == "string" then
+				event:reply(ret);
+			end
+			return ret;
+		end
+	end
+	
+	-- Hook messages to bot and from rooms, fire a command event on the bot
+	bot:hook("message", process_command);
+	
+	bot:hook("groupchat/joining", function (room)
+		room:hook("message", process_command);
+	end);
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/groupchat.lua	Mon Dec 21 19:27:08 2009 +0000
@@ -0,0 +1,115 @@
+local events = require "events";
+local st = require "util.stanza";
+
+local room_mt = {};
+room_mt.__index = room_mt;
+
+local xmlns_delay = "urn:xmpp:delay";
+
+function riddim.plugins.groupchat(bot)
+	bot.rooms = {};
+	
+	bot.stream:hook("stanza", function (stanza)
+		local room_jid = jid.bare(stanza.attr.from);
+		local room = bot.rooms[room_jid]
+		if room then
+			local nick = select(3, jid.split(stanza.attr.from));
+			local body = stanza:get_child("body");
+			local delay = stanza:get_child("delay", xmlns_delay);
+			local event = {
+					room_jid = room_jid;
+					room = room;
+					sender = room.occupants[nick];
+					nick = nick;
+					body = (body and body:get_text()) or nil;
+					stanza = stanza;
+					delay = (delay and delay.attr.stamp);
+				};
+			if stanza.name == "message" then
+				local replied;
+				local r = st.reply(stanza);
+				if stanza.attr.type == "groupchat" then
+					r.attr.type = stanza.attr.type;
+					r.attr.to = jid.bare(stanza.attr.to);
+				end
+				function event:reply(reply)
+					if not reply then reply = "Nothing to say to you"; end
+					if replied then return false; end
+					replied = true;
+					if r.attr.type == "groupchat" then
+						reply = event.sender.nick..": "..reply;
+					end
+					room:send(r:tag("body"):text(reply));
+				end
+			end
+			local ret;
+			if stanza.name ~= "message" or nick ~= room.nick then
+				ret = room:event(stanza.name, event);
+			end
+			return ret or (stanza.name == "message") or nil;
+		end
+	end, 500);
+	
+	function bot:join_room(jid, nick)
+		nick = nick or bot.config.nick or ("bot"..math.random(10000,99999));
+		local room = setmetatable({
+			bot = bot, jid = jid, nick = nick,
+			occupants = {},
+			events = events.new() }, room_mt);
+		self.rooms[jid] = room;
+		local occupants = room.occupants;
+		room:hook("presence", function (presence)
+			local nick = presence.nick;
+			if not occupants[nick] then
+				if presence.type ~= "unavailable" then
+					occupants[nick] = {
+						nick = nick;
+						jid = presence.stanza.attr.from;
+						presence = presence.stanza;
+						};
+					if nick == room.nick then
+						room.bot:event("groupchat/joined", room);
+					else
+						room:event("occupant-joined", occupants[nick]);
+					end
+				else
+					occupants[nick].presence = presence.stanza;
+					room:event("occupant-left", occupants[nick]);
+					occupants[nick] = nil;
+				end
+			end
+		end);
+		self:send(st.presence({to = jid.."/"..nick}));
+		self:event("groupchat/joining", room);
+		return room;
+	end
+end
+
+function room_mt:send(stanza)
+	if stanza.name == "message" and not stanza.attr.type then
+		stanza.attr.type = "groupchat";
+	end
+	if stanza.attr.type == "groupchat" then
+		stanza.attr.to = self.jid;
+	end
+	self.bot:send(stanza);
+end
+
+function room_mt:send_message(text)
+	self:send(st.message():tag("body"):text(text));
+end
+
+function room_mt:leave(message)
+	self.bot:event("groupchat/leaving", room);
+	self:send(st.presence({type="unavailable"}));
+	self.bot:event("groupchat/left", room);
+end
+
+function room_mt:event(name, arg)
+	self.bot.stream:debug("Firing room event: %s", name);
+	return self.events.fire_event(name, arg);
+end
+
+function room_mt:hook(name, callback, priority)
+	return self.events.add_handler(name, callback, priority);
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/ping.lua	Mon Dec 21 19:27:08 2009 +0000
@@ -0,0 +1,14 @@
+
+function riddim.plugins.ping(bot)
+	bot.stream:add_plugin("ping");
+	bot:hook("commands/ping", function (command)
+			local jid = command.param;
+			if jid then
+				bot.stream:ping(jid, function (time)
+					command:reply(string.format("Pong from %s in %0.3f seconds", jid, time));
+				end);
+				return true;
+			end
+			return "pong";
+		end);
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/version.lua	Mon Dec 21 19:27:08 2009 +0000
@@ -0,0 +1,50 @@
+local st = require "util.stanza";
+require "util.xstanza";
+
+local xmlns_version = "jabber:iq:version";
+
+function riddim.plugins.version(bot)
+	bot.stream:add_plugin("version");
+	bot.stream.version:set{
+		name = "Riddim";
+		version = "alpha";
+	};
+
+	bot:hook("commands/version", function (command)
+		local who, param = bot.stream.jid, command.param or (command.room and command.room.nick);
+		if param then
+			if command.room and command.room.occupants[param] then
+				who = command.room.occupants[param].jid;
+			elseif command.room and command.room.occupants[param:gsub("%s$", "")] then
+				who = command.room.occupants[param:gsub("%s$", "")].jid;
+			else
+				who = param;
+			end
+		end
+		
+		bot.stream:query_version(who,
+			function (reply)
+				if not reply.error then
+					local saywho = (who == command.sender.jid and "You are") or (param.." is");
+					command:reply(saywho.." running "..(reply.name or "something")
+						.." version "..(reply.version or "unknown")
+						.." on "..(reply.platform or "an unknown platform"));
+				else
+					local type, condition, text = reply.type, reply.condition, reply.text;
+					local r = "There was an error requesting "..param.."'s version";
+					if condition == "service-unavailable" then
+						r = param.." doesn't reply to version requests";
+					elseif condition == "remote-server-not-found" then
+						r = param.." can't be reached via XMPP"
+					elseif condition and not text then
+						r = r..": "..condition;
+					end
+					if text then
+						r = r .. " ("..text..")";
+					end
+					command:reply(r);
+				end
+			end);
+		return true;
+	end);	
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/squishy	Mon Dec 21 19:27:08 2009 +0000
@@ -0,0 +1,8 @@
+Module "riddim.plugins.groupchat" "plugins/groupchat.lua"
+Module "riddim.plugins.commands"  "plugins/commands.lua"
+Module "riddim.plugins.version"   "plugins/version.lua"
+Module "riddim.plugins.ping"      "plugins/ping.lua"
+
+Main "init.lua"
+
+Output "riddim.lua"

mercurial