examples/xep0163.lua
author Myhailo Danylenko <isbear@ukrpost.net>
Mon, 16 Mar 2009 04:06:02 +0200
changeset 29 0199ecce6c11
parent 27 92b254b64360
child 31 54957980a83a
permissions -rw-r--r--
Timers destruction * timers destruction on unloading * option to enable lm debug output * some other options


dopath 'mpd'

tune_enabled = false

mpd_pub_song = { }

function mpd_getstatus ()
	local status   = mpd.call_command { 'status' }
	if not tune_enabled or ( status.state ~= 'play' and status.state ~= 'pause' ) then
		if mpd_pub_song.artist or mpd_pub_song.length or mpd_pub_song.source or mpd_pub_song.title or mpd_pub_song.track then
			mpd_pub_song.artist = nil
			mpd_pub_song.length = nil
			mpd_pub_song.source = nil
			mpd_pub_song.title  = nil
			mpd_pub_song.track  = nil
			return mpd_pub_song
		else
			return nil
		end
	end
	
	local modified  = false
	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 artist, length, source, title, track = song.artist, song.time, song.album, song.title, song.id

	if not artist or artist == '' then
		artist = 'Unknown'
	end
	if not mpd_pub_song.artist or artist ~= mpd_pub_song.artist[1] then
		mpd_pub_song.artist = { artist }
		modified = true
	end

	if length and length ~= 0 then
		if not mpd_pub_song.length or length ~= mpd_pub_song.length[1] then
			mpd_pub_song.length = { length }
			modified = true
		end
	elseif mpd_pub_song.length then -- no length
		mpd_pub_song.length = nil
		modified = true
	end

	if not source or source == '' then
		source = dir
	end
	if not mpd_pub_song.source or source ~= mpd_pub_song.source[1] then
		mpd_pub_song.source = { source }
		modified = true
	end

	if not title or title == '' then
		title = file
	end
	if not mpd_pub_song.title or title ~= mpd_pub_song.title[1] then
		mpd_pub_song.title = { title }
		modified = true
	end

	if not mpd_pub_song.track or track ~= mpd_pub_song.track[1] then
		mpd_pub_song.track = { track }
		modified = true
	end

	if modified then
		return mpd_pub_song
	else
		return nil
	end
end

function pep_publish ( node, data )
	local conn = lm.connection.bless ( main.connection () )
	data.xmlns = 'http://jabber.org/protocol/' .. node -- this may modify original data? imo it does not matter.
	if conn:status () == 'authenticated' then
--		local bjid = conn:jid():gsub ( '/.*', '' )
		conn:send (
			lm.message.create { mtype = 'iq-set', -- from = conn:jid (), to = bjid,
				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' },
						}},
					},
				},
--]]
			},
			function ( conn, mess )
				local mtype, smtype = mess:type ()
				if smtype == 'result' then
					return true
				elseif smtype == 'error' then
					print ( 'Error publishing to ' .. node .. ': ' .. mess:xml () ) -- FIXME
					return true
				else
					print ( 'Weird ansver to publishing request: ' .. mess:xml () )
					return false
				end
			end )
	end
end

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 enable = yesno ( main.option ( 'lua_pep_notification' ) )
			if enable == false then
				return true
			end
			local is = e:child ( 'items' )
			if is then
				local from = mess:attribute ( 'from' )
				local node = is:attribute ( 'node' )
				if node == 'http://jabber.org/protocol/tune' then
					local tune = is:path ( 'item', 'tune' )
					if tune then
						local item = tune: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 listening to:' .. text
						else
							text = 'Now not listening to anything'
						end
						main.print_info ( from, text )
						return true
					else
						main.print_info ( from, 'Strange: no tune item in pep notification' )
					end
				elseif node == 'http://jabber.org/protocol/mood' then
					local mood = is:path ( 'item', 'mood' )
					if mood then
						local item = mood: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
						return true
					else
						main.print_info ( from, 'Strange: no mood item in pep notification' )
					end
				elseif node == 'http://jabber.org/protocol/activity' then
					local activity = is:path ( 'item', 'activity' )
					if activity then
						local item = activity: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
					else
						main.print_info ( from, 'Strange: no activity item in pep notification' )
					end
				elseif node == 'http://jabber.org/protocol/geoloc' then
					local loc = is:path ( 'item', 'geoloc' )
					if loc then
						local item = loc: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
					else
						main.print_info ( from, 'Strange: no geoloc item in pep notification' )
					end
				else
					main.print_info ( from, 'Unknown node in pep notification: ' .. is:xml () )
				end
			end
		end
		return false
	end )


function mpd_callback ()
	local sdata = mpd_getstatus ()
	if sdata then
		pep_publish ( 'tune', sdata )
	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
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 = 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, boolean_cid )
main.command ( 'mood',
	function ( args )
		args = main.parse_args ( 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 ( 'mood', data )
	end )
main.command ( 'activity',
	function ( args )
		args = main.parse_args ( 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 ( 'activity', data )
	end )
main.command ( 'location',
	function ( args )
		args = main.parse_args ( args )
		local data = { }
		for key, val in pairs ( args ) do
			data[key] = { val }
		end
		pep_publish ( 'geoloc', data )
	end )

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."

pep_handler_registered = false

hooks_d['hook-post-connect'].xep0163 =
	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'].xep0163 =
			function ( args )
				if tune_enabled then
					mpd_callback ()
				end
			end
		hooks_d['hook-quit'].xep0163 =
			function ( args )
				if mpd_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: --