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