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