examples/pep.lua
changeset 46 c2210e9b3e34
child 56 8561e55e0662
equal deleted inserted replaced
45:ff175dcd79cd 46:c2210e9b3e34
       
     1 
       
     2 -- PERSONAL EVENTING PROTOCOL (XEP-0163)
       
     3 
       
     4 -- library
       
     5 
       
     6 require 'lm'
       
     7 require 'iq'
       
     8 require 'mpd'
       
     9 
       
    10 -- public
       
    11 
       
    12 pep = {
       
    13 	handlers = {},
       
    14 }
       
    15 
       
    16 function pep.publish ( conn, node, data, success, fail )
       
    17 --	local bjid = conn:jid():gsub ( '/.*', '' )
       
    18 	data.xmlns = 'http://jabber.org/protocol/' .. node
       
    19 	iq.send ( conn, nil, 'set',
       
    20 		{
       
    21 			pubsub = { xmlns = 'http://jabber.org/protocol/pubsub',
       
    22 				publish = { node = 'http://jabber.org/protocol/' .. node,
       
    23 					item = { -- id = "current",
       
    24 						[node] = data,
       
    25 					},
       
    26 				},
       
    27 			},
       
    28 --[[
       
    29 			configure = {
       
    30 				x = {
       
    31 					field = {{ type = "hidden", var = 'FORM_TYPE',
       
    32 						value = { 'http://jabber.org/protocol/pubsub#node_config' },
       
    33 					},{ var = "pubsub#access_model",
       
    34 						value = { 'presence' },
       
    35 					}},
       
    36 				},
       
    37 			},
       
    38 --]]
       
    39 		}, success, fail )
       
    40 end
       
    41 
       
    42 -- private
       
    43 
       
    44 -- XXX in fact, it is not a pep handler, it is pubsub handler.
       
    45 --     should it go there?
       
    46 local pep_handler_registered       = false
       
    47 local pep_incoming_message_handler = lm.message_handler.new (
       
    48 	function ( conn, mess )
       
    49 		local e = mess:child ( 'event' )
       
    50 		if e and e:attribute ( 'xmlns' ) == 'http://jabber.org/protocol/pubsub#event' then
       
    51 			local is = e:child ( 'items' )
       
    52 			if is then
       
    53 				local from = mess:attribute ( 'from' )
       
    54 				local node = is:attribute ( 'node' )
       
    55 				local item = is:child ( 'item' )
       
    56 				if item then
       
    57 					local handled = true -- XXX should we do this? well, if it becomes general pubsub handler - then no.
       
    58 					local n       = item:children ()
       
    59 					while n do
       
    60 						local xmlns = n:attribute ( 'xmlns' )
       
    61 						if pep.handlers[xmlns] then
       
    62 							if not pep.handlers[xmlns] ( from, node, n ) then
       
    63 								handled = false
       
    64 							end
       
    65 						else
       
    66 							handled = false
       
    67 						end
       
    68 						n = n:next ()
       
    69 					end
       
    70 					return handled
       
    71 				end
       
    72 			end
       
    73 		end
       
    74 		return false
       
    75 	end )
       
    76 
       
    77 -- mcabber
       
    78 
       
    79 local tune_enabled = false
       
    80 local mpd_pub_song = { }
       
    81 
       
    82 -- XXX in fact, we should separate library code from client code here, but it is silly.
       
    83 --     seting up four registrators, four default handlers, four handler storage variables...
       
    84 --     and, in fact, we cannot do much about complex datatypes of mood and activity.
       
    85 pep.handlers['http://jabber.org/protocol/tune'] =
       
    86 		function ( from, node, data )
       
    87 			local self = false
       
    88 			if from == lm.connection.bless ( main.connection () ):jid():gsub ( '/.*', '' ) then -- o_O
       
    89 				self         = true
       
    90 				mpd_pub_song = { }
       
    91 			end
       
    92 			local item = data:children ()
       
    93 			local text = ''
       
    94 			while item do
       
    95 				local name  = item:name ()
       
    96 				local value = item:value ()
       
    97 				if self then
       
    98 					mpd_pub_song[name] = { value or '' }
       
    99 				end
       
   100 				text = ("%s\n- %s: %s"):format ( text, item:name (), item:value () or '' )
       
   101 				item = item:next ()
       
   102 			end
       
   103 			if main.yesno ( main.option ( 'lua_pep_notification' ) ) then
       
   104 				if text ~= '' then
       
   105 					text = 'Now listening to:' .. text
       
   106 				else
       
   107 					text = 'Now not listening to anything'
       
   108 				end
       
   109 			end
       
   110 			main.print_info ( from, text )
       
   111 			return true
       
   112 		end
       
   113 pep.handlers['http://jabber.org/protocol/mood'] =
       
   114 		function ( from, node, data )
       
   115 			if not main.yesno ( main.option ( 'lua_pep_notification' ) ) then
       
   116 				return true
       
   117 			end
       
   118 			local item = data:children ()
       
   119 			local mood, desc
       
   120 			while item do
       
   121 				if item:name () == 'text' then
       
   122 					desc = item:value ()
       
   123 				else
       
   124 					mood = item:name ()
       
   125 					-- here we can add child elements handling (by namespace)
       
   126 				end
       
   127 				item = item:next ()
       
   128 			end
       
   129 			if mood then
       
   130 				main.print_info ( from, ("Buddy's mood now %s %s"):format ( mood, desc or '' ) )
       
   131 			else
       
   132 				main.print_info ( from, "Buddy hides his mood" )
       
   133 			end
       
   134 		end
       
   135 pep.handlers['http://jabber.org/protocol/activity'] =
       
   136 		function ( from, node, data )
       
   137 			if not main.yesno ( main.option ( 'lua_pep_notification' ) ) then
       
   138 				return true
       
   139 			end
       
   140 			local item = data:children ()
       
   141 			local activity, desc
       
   142 			while item do
       
   143 				if item:name () == 'text' then
       
   144 					desc = item:value ()
       
   145 				else
       
   146 					activity = item:name ()
       
   147 					local subitem = item:children ()
       
   148 					if subitem then
       
   149 						-- here we can check for non-standard subactivity elements,
       
   150 						-- add subactivity child elements handling
       
   151 						activity = ("%s: %s"):format ( activity, subitem:name () )
       
   152 					end
       
   153 				end
       
   154 				item = item:next ()
       
   155 			end
       
   156 			if activity then
       
   157 				main.print_info ( from, ("Now %s %s"):format ( activity, desc or '' ) )
       
   158 			else
       
   159 				main.print_info ( from, "Buddy hides his activity" )
       
   160 			end
       
   161 			return true
       
   162 		end
       
   163 pep.handlers['http://jabber.org/protocol/geoloc'] =
       
   164 		function ( from, node, data )
       
   165 			if not main.yesno ( main.option ( 'lua_pep_notification' ) ) then
       
   166 				return true
       
   167 			end
       
   168 			local item = data:children ()
       
   169 			local text = ''
       
   170 			while item do
       
   171 				text = ("%s\n- %s: %s"):format ( text, item:name (), item:value () or '' )
       
   172 				item = item:next ()
       
   173 			end
       
   174 			if text ~= '' then
       
   175 				text = 'Now at:' .. text
       
   176 			else
       
   177 				text = 'Now in unknown location'
       
   178 			end
       
   179 			main.print_info ( from, text )
       
   180 			return true
       
   181 		end
       
   182 
       
   183 local function mpd_getstatus ()
       
   184 	local status = mpd.call_command { 'status' }
       
   185 	if not tune_enabled or ( status.state ~= 'play' and status.state ~= 'pause' ) then
       
   186 		for k, v in pairs ( mpd_pub_song ) do -- if there is anything published, publish nothing
       
   187 			return { }
       
   188 		end
       
   189 		return nil
       
   190 	end
       
   191 	
       
   192 	local song      = mpd.call_command { 'currentsong' }
       
   193 	local dir, file = song.file:match ( '(.+)/(.-)' )
       
   194 	-- populate according to currentsong fields: artist - artist, length - time, source - album, title - title, track - id, rating - ?, uri - ?
       
   195 	local ret = {
       
   196 		artist = { song.artist or 'Unknown' },
       
   197 		length = { song.time                },
       
   198 		source = { song.album  or dir       },
       
   199 		title  = { song.title  or file      },
       
   200 		track  = { song.id                  },
       
   201 	}
       
   202 
       
   203 	if not song.time or song.time == '0' then -- XXX
       
   204 		ret.length = nil
       
   205 	end
       
   206 
       
   207 	local modified = false
       
   208 	for k, v in pairs ( ret ) do
       
   209 		if not mpd_pub_song[k] or mpd_pub_song[k][1] ~= v[1] then
       
   210 			modified = true
       
   211 			break
       
   212 		end
       
   213 	end
       
   214 	if not modified then
       
   215 		for k, v in pairs ( mpd_pub_song ) do
       
   216 			if not ret[k] or ret[k][1] ~= v[1] then
       
   217 				modified = true
       
   218 				break
       
   219 			end
       
   220 		end
       
   221 	end
       
   222 
       
   223 	if modified then
       
   224 		return ret
       
   225 	else
       
   226 		return nil
       
   227 	end
       
   228 end
       
   229 
       
   230 local function dummy ()
       
   231 end
       
   232 
       
   233 local function mpd_callback ()
       
   234 	local sdata = mpd_getstatus ()
       
   235 	if sdata then
       
   236 		pep.publish ( lm.connection.bless ( main.connection () ), 'tune', sdata, dummy, dummy )
       
   237 	end
       
   238 	if tune_enabled then
       
   239 		return true
       
   240 	else
       
   241 		return false
       
   242 	end
       
   243 end
       
   244 
       
   245 -- do not call it too fast, or you end up with many daemons at once
       
   246 local function enable_tune ( yn )
       
   247 	if yn == nil then
       
   248 		yn = true
       
   249 	end
       
   250 	if yn then
       
   251 		if not tune_enabled then
       
   252 			main.timer ( 15, mpd_callback )
       
   253 			tune_enabled = true
       
   254 			-- update status
       
   255 		end
       
   256 	else
       
   257 		if tune_enabled then
       
   258 			tune_enabled = false
       
   259 			-- update status
       
   260 		end
       
   261 	end
       
   262 end
       
   263 
       
   264 main.command ( 'tune',
       
   265 	function ( args )
       
   266 		local enable = main.yesno ( args )
       
   267 		if enable == nil then
       
   268 			if tune_enabled then
       
   269 				print ( "Tune notifications enabled" )
       
   270 			else
       
   271 				print ( "Tune notifications disabled" )
       
   272 			end
       
   273 		else
       
   274 			enable_tune ( enable )
       
   275 		end
       
   276 	end, false, 'yesno' )
       
   277 main.command ( 'mood',
       
   278 	function ( args )
       
   279 		local data = { }
       
   280 		local mood, text = args[1], args[2]
       
   281 		if text then
       
   282 			data.text  = { text }
       
   283 		end
       
   284 		if mood then
       
   285 			data[mood] = { }
       
   286 		end
       
   287 		pep.publish ( lm.connection.bless ( main.connection () ), 'mood', data, dummy, dummy )
       
   288 	end, true )
       
   289 main.command ( 'activity',
       
   290 	function ( args )
       
   291 		local data = { }
       
   292 		local activity, text = args[1], args[2]
       
   293 		if text then
       
   294 			data.text = { text }
       
   295 		end
       
   296 		local act, subact = activity:match ( "(.-)%-(.+)" )
       
   297 		if not act then
       
   298 			act = activity
       
   299 		end
       
   300 		if act ~= '' then
       
   301 			data[act] = { }
       
   302 			if subact then
       
   303 				data[act][subact] = { }
       
   304 			end
       
   305 		end
       
   306 		pep.publish ( lm.connection.bless ( main.connection () ), 'activity', data, dummy, dummy )
       
   307 	end, true )
       
   308 main.command ( 'location',
       
   309 	function ( args )
       
   310 		local data = { }
       
   311 		for key, val in pairs ( args ) do
       
   312 			data[key] = { val }
       
   313 		end
       
   314 		pep.publish ( lm.connection.bless ( main.connection () ), 'geoloc', data, dummy, dummy )
       
   315 	end, true )
       
   316 
       
   317 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)."
       
   318 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."
       
   319 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."
       
   320 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."
       
   321 
       
   322 hooks_d['hook-post-connect'].pep =
       
   323 	function ( args )
       
   324 		lm.connection.bless( main.connection () ):handler ( pep_incoming_message_handler, 'message', 'normal' )
       
   325 		pep_handler_registered = true
       
   326 		if tune_enabled then
       
   327 			mpd_callback ()
       
   328 		end
       
   329 		-- XXX may it confuse pairs()?
       
   330 		hooks_d['hook-post-connect'].pep =
       
   331 			function ( args )
       
   332 				if tune_enabled then
       
   333 					mpd_callback ()
       
   334 				end
       
   335 			end
       
   336 		hooks_d['hook-quit'].pep =
       
   337 			function ( args )
       
   338 				if pep_handler_registered then
       
   339 					lm.connection.bless( main.connection () ):handler ( pep_incoming_message_handler, 'message' )
       
   340 					pep_handler_registered = false
       
   341 				end
       
   342 			end
       
   343 	end
       
   344 
       
   345 -- XXX this really should be initialized after connection establishment (?)
       
   346 --     but as this thing is implemented by now, it will be cached by server,
       
   347 --     and, thus, we will be unable to get notifications.
       
   348 main.add_feature ( 'http://jabber.org/protocol/tune+notify' )
       
   349 main.add_feature ( 'http://jabber.org/protocol/tune' )
       
   350 main.add_feature ( 'http://jabber.org/protocol/mood+notify' )
       
   351 main.add_feature ( 'http://jabber.org/protocol/mood' )
       
   352 main.add_feature ( 'http://jabber.org/protocol/activity+notify' )
       
   353 main.add_feature ( 'http://jabber.org/protocol/activity' )
       
   354 main.add_feature ( 'http://jabber.org/protocol/geoloc+notify' )
       
   355 main.add_feature ( 'http://jabber.org/protocol/geoloc' )
       
   356 
       
   357 -- vim: se ts=4: --