-- 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: --