Pep in library style
authorMyhailo Danylenko <isbear@ukrpost.net>
Sat, 21 Mar 2009 14:06:42 +0200
changeset 46 c2210e9b3e34
parent 45 ff175dcd79cd
child 47 aba4bbe32cba
Pep in library style
examples/mcabberrc.lua
examples/pep.lua
--- a/examples/mcabberrc.lua	Sat Mar 21 05:06:25 2009 +0200
+++ b/examples/mcabberrc.lua	Sat Mar 21 14:06:42 2009 +0200
@@ -279,7 +279,7 @@
 
 -- PERSONAL EVENTING PROTOCOL (XEP-0163)
 
-dopath 'xep0163'
+dopath 'pep'
 
 -- JOBS
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/pep.lua	Sat Mar 21 14:06:42 2009 +0200
@@ -0,0 +1,357 @@
+
+-- PERSONAL EVENTING PROTOCOL (XEP-0163)
+
+-- library
+
+require 'lm'
+require 'iq'
+require 'mpd'
+
+-- public
+
+pep = {
+	handlers = {},
+}
+
+function pep.publish ( conn, node, data, success, fail )
+--	local bjid = conn:jid():gsub ( '/.*', '' )
+	data.xmlns = 'http://jabber.org/protocol/' .. node
+	iq.send ( conn, nil, 'set',
+		{
+			pubsub = { xmlns = 'http://jabber.org/protocol/pubsub',
+				publish = { node = 'http://jabber.org/protocol/' .. node,
+					item = { -- id = "current",
+						[node] = data,
+					},
+				},
+			},
+--[[
+			configure = {
+				x = {
+					field = {{ type = "hidden", var = 'FORM_TYPE',
+						value = { 'http://jabber.org/protocol/pubsub#node_config' },
+					},{ var = "pubsub#access_model",
+						value = { 'presence' },
+					}},
+				},
+			},
+--]]
+		}, success, fail )
+end
+
+-- private
+
+-- XXX in fact, it is not a pep handler, it is pubsub handler.
+--     should it go there?
+local pep_handler_registered       = false
+local pep_incoming_message_handler = lm.message_handler.new (
+	function ( conn, mess )
+		local e = mess:child ( 'event' )
+		if e and e:attribute ( 'xmlns' ) == 'http://jabber.org/protocol/pubsub#event' then
+			local is = e:child ( 'items' )
+			if is then
+				local from = mess:attribute ( 'from' )
+				local node = is:attribute ( 'node' )
+				local item = is:child ( 'item' )
+				if item then
+					local handled = true -- XXX should we do this? well, if it becomes general pubsub handler - then no.
+					local n       = item:children ()
+					while n do
+						local xmlns = n:attribute ( 'xmlns' )
+						if pep.handlers[xmlns] then
+							if not pep.handlers[xmlns] ( from, node, n ) then
+								handled = false
+							end
+						else
+							handled = false
+						end
+						n = n:next ()
+					end
+					return handled
+				end
+			end
+		end
+		return false
+	end )
+
+-- mcabber
+
+local tune_enabled = false
+local mpd_pub_song = { }
+
+-- XXX in fact, we should separate library code from client code here, but it is silly.
+--     seting up four registrators, four default handlers, four handler storage variables...
+--     and, in fact, we cannot do much about complex datatypes of mood and activity.
+pep.handlers['http://jabber.org/protocol/tune'] =
+		function ( from, node, data )
+			local self = false
+			if from == lm.connection.bless ( main.connection () ):jid():gsub ( '/.*', '' ) then -- o_O
+				self         = true
+				mpd_pub_song = { }
+			end
+			local item = data:children ()
+			local text = ''
+			while item do
+				local name  = item:name ()
+				local value = item:value ()
+				if self then
+					mpd_pub_song[name] = { value or '' }
+				end
+				text = ("%s\n- %s: %s"):format ( text, item:name (), item:value () or '' )
+				item = item:next ()
+			end
+			if main.yesno ( main.option ( 'lua_pep_notification' ) ) then
+				if text ~= '' then
+					text = 'Now listening to:' .. text
+				else
+					text = 'Now not listening to anything'
+				end
+			end
+			main.print_info ( from, text )
+			return true
+		end
+pep.handlers['http://jabber.org/protocol/mood'] =
+		function ( from, node, data )
+			if not main.yesno ( main.option ( 'lua_pep_notification' ) ) then
+				return true
+			end
+			local item = data:children ()
+			local mood, desc
+			while item do
+				if item:name () == 'text' then
+					desc = item:value ()
+				else
+					mood = item:name ()
+					-- here we can add child elements handling (by namespace)
+				end
+				item = item:next ()
+			end
+			if mood then
+				main.print_info ( from, ("Buddy's mood now %s %s"):format ( mood, desc or '' ) )
+			else
+				main.print_info ( from, "Buddy hides his mood" )
+			end
+		end
+pep.handlers['http://jabber.org/protocol/activity'] =
+		function ( from, node, data )
+			if not main.yesno ( main.option ( 'lua_pep_notification' ) ) then
+				return true
+			end
+			local item = data:children ()
+			local activity, desc
+			while item do
+				if item:name () == 'text' then
+					desc = item:value ()
+				else
+					activity = item:name ()
+					local subitem = item:children ()
+					if subitem then
+						-- here we can check for non-standard subactivity elements,
+						-- add subactivity child elements handling
+						activity = ("%s: %s"):format ( activity, subitem:name () )
+					end
+				end
+				item = item:next ()
+			end
+			if activity then
+				main.print_info ( from, ("Now %s %s"):format ( activity, desc or '' ) )
+			else
+				main.print_info ( from, "Buddy hides his activity" )
+			end
+			return true
+		end
+pep.handlers['http://jabber.org/protocol/geoloc'] =
+		function ( from, node, data )
+			if not main.yesno ( main.option ( 'lua_pep_notification' ) ) then
+				return true
+			end
+			local item = data:children ()
+			local text = ''
+			while item do
+				text = ("%s\n- %s: %s"):format ( text, item:name (), item:value () or '' )
+				item = item:next ()
+			end
+			if text ~= '' then
+				text = 'Now at:' .. text
+			else
+				text = 'Now in unknown location'
+			end
+			main.print_info ( from, text )
+			return true
+		end
+
+local function mpd_getstatus ()
+	local status = mpd.call_command { 'status' }
+	if not tune_enabled or ( status.state ~= 'play' and status.state ~= 'pause' ) then
+		for k, v in pairs ( mpd_pub_song ) do -- if there is anything published, publish nothing
+			return { }
+		end
+		return nil
+	end
+	
+	local song      = mpd.call_command { 'currentsong' }
+	local dir, file = song.file:match ( '(.+)/(.-)' )
+	-- populate according to currentsong fields: artist - artist, length - time, source - album, title - title, track - id, rating - ?, uri - ?
+	local ret = {
+		artist = { song.artist or 'Unknown' },
+		length = { song.time                },
+		source = { song.album  or dir       },
+		title  = { song.title  or file      },
+		track  = { song.id                  },
+	}
+
+	if not song.time or song.time == '0' then -- XXX
+		ret.length = nil
+	end
+
+	local modified = false
+	for k, v in pairs ( ret ) do
+		if not mpd_pub_song[k] or mpd_pub_song[k][1] ~= v[1] then
+			modified = true
+			break
+		end
+	end
+	if not modified then
+		for k, v in pairs ( mpd_pub_song ) do
+			if not ret[k] or ret[k][1] ~= v[1] then
+				modified = true
+				break
+			end
+		end
+	end
+
+	if modified then
+		return ret
+	else
+		return nil
+	end
+end
+
+local function dummy ()
+end
+
+local function mpd_callback ()
+	local sdata = mpd_getstatus ()
+	if sdata then
+		pep.publish ( lm.connection.bless ( main.connection () ), 'tune', sdata, dummy, dummy )
+	end
+	if tune_enabled then
+		return true
+	else
+		return false
+	end
+end
+
+-- do not call it too fast, or you end up with many daemons at once
+local function enable_tune ( yn )
+	if yn == nil then
+		yn = true
+	end
+	if yn then
+		if not tune_enabled then
+			main.timer ( 15, mpd_callback )
+			tune_enabled = true
+			-- update status
+		end
+	else
+		if tune_enabled then
+			tune_enabled = false
+			-- update status
+		end
+	end
+end
+
+main.command ( 'tune',
+	function ( args )
+		local enable = main.yesno ( args )
+		if enable == nil then
+			if tune_enabled then
+				print ( "Tune notifications enabled" )
+			else
+				print ( "Tune notifications disabled" )
+			end
+		else
+			enable_tune ( enable )
+		end
+	end, false, 'yesno' )
+main.command ( 'mood',
+	function ( args )
+		local data = { }
+		local mood, text = args[1], args[2]
+		if text then
+			data.text  = { text }
+		end
+		if mood then
+			data[mood] = { }
+		end
+		pep.publish ( lm.connection.bless ( main.connection () ), 'mood', data, dummy, dummy )
+	end, true )
+main.command ( 'activity',
+	function ( args )
+		local data = { }
+		local activity, text = args[1], args[2]
+		if text then
+			data.text = { text }
+		end
+		local act, subact = activity:match ( "(.-)%-(.+)" )
+		if not act then
+			act = activity
+		end
+		if act ~= '' then
+			data[act] = { }
+			if subact then
+				data[act][subact] = { }
+			end
+		end
+		pep.publish ( lm.connection.bless ( main.connection () ), 'activity', data, dummy, dummy )
+	end, true )
+main.command ( 'location',
+	function ( args )
+		local data = { }
+		for key, val in pairs ( args ) do
+			data[key] = { val }
+		end
+		pep.publish ( lm.connection.bless ( main.connection () ), 'geoloc', data, dummy, dummy )
+	end, true )
+
+commands_help['tune']     = "[enable|disable|on|off|yes|no|true|false]\n\nEnables or disables publishing of notifications about playing music in your player (currently only mpd is supported)."
+commands_help['mood']     = "[mood [message]]\n\nPublishes your mood.\nNote, that for now it does not checks for mood validity, so, see xep0107 for valid moods."
+commands_help['activity'] = "[activity[-specific_activity] [text]]\n\nPublishes your activity.\nNote, that for now it does not checks for activity validity, so, see xep0108 for valid activity values."
+commands_help['location'] = "[-key value [-key value ...]]\n\nPublishes your current geolocation.\nValid keys are accuracy, alt, area, bearing, building, country, datum, description, error, floor, lat, locality, lon, postalcode, region, room, speed, street, text, timestamp and uri, according to xep0080."
+
+hooks_d['hook-post-connect'].pep =
+	function ( args )
+		lm.connection.bless( main.connection () ):handler ( pep_incoming_message_handler, 'message', 'normal' )
+		pep_handler_registered = true
+		if tune_enabled then
+			mpd_callback ()
+		end
+		-- XXX may it confuse pairs()?
+		hooks_d['hook-post-connect'].pep =
+			function ( args )
+				if tune_enabled then
+					mpd_callback ()
+				end
+			end
+		hooks_d['hook-quit'].pep =
+			function ( args )
+				if pep_handler_registered then
+					lm.connection.bless( main.connection () ):handler ( pep_incoming_message_handler, 'message' )
+					pep_handler_registered = false
+				end
+			end
+	end
+
+-- XXX this really should be initialized after connection establishment (?)
+--     but as this thing is implemented by now, it will be cached by server,
+--     and, thus, we will be unable to get notifications.
+main.add_feature ( 'http://jabber.org/protocol/tune+notify' )
+main.add_feature ( 'http://jabber.org/protocol/tune' )
+main.add_feature ( 'http://jabber.org/protocol/mood+notify' )
+main.add_feature ( 'http://jabber.org/protocol/mood' )
+main.add_feature ( 'http://jabber.org/protocol/activity+notify' )
+main.add_feature ( 'http://jabber.org/protocol/activity' )
+main.add_feature ( 'http://jabber.org/protocol/geoloc+notify' )
+main.add_feature ( 'http://jabber.org/protocol/geoloc' )
+
+-- vim: se ts=4: --