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