Software /
code /
prosody
Comparison
plugins/muc/muc.lib.lua @ 6215:1dd09dc04945
plugins/muc: Move history to an external module
This resulted in the split up of the main muc-occupant-joined event handler into 3 seperate ones, handling occupant list, history and subject
author | daurnimator <quae@daurnimator.com> |
---|---|
date | Thu, 03 Apr 2014 15:14:52 -0400 |
parent | 6214:9813c74ce006 |
child | 6216:50b8b7caf200 |
comparison
equal
deleted
inserted
replaced
6214:9813c74ce006 | 6215:1dd09dc04945 |
---|---|
9 | 9 |
10 local select = select; | 10 local select = select; |
11 local pairs, ipairs = pairs, ipairs; | 11 local pairs, ipairs = pairs, ipairs; |
12 local next = next; | 12 local next = next; |
13 local setmetatable = setmetatable; | 13 local setmetatable = setmetatable; |
14 local t_insert, t_remove = table.insert, table.remove; | |
15 | |
16 local gettime = os.time; | |
17 local datetime = require "util.datetime"; | |
18 | 14 |
19 local dataform = require "util.dataforms"; | 15 local dataform = require "util.dataforms"; |
20 | |
21 local jid_split = require "util.jid".split; | 16 local jid_split = require "util.jid".split; |
22 local jid_bare = require "util.jid".bare; | 17 local jid_bare = require "util.jid".bare; |
23 local jid_prep = require "util.jid".prep; | 18 local jid_prep = require "util.jid".prep; |
24 local st = require "util.stanza"; | 19 local st = require "util.stanza"; |
25 local log = require "util.logger".init("mod_muc"); | 20 local log = require "util.logger".init("mod_muc"); |
26 local base64 = require "util.encodings".base64; | 21 local base64 = require "util.encodings".base64; |
27 local md5 = require "util.hashes".md5; | 22 local md5 = require "util.hashes".md5; |
28 | 23 |
29 local occupant_lib = module:require "muc/occupant" | 24 local occupant_lib = module:require "muc/occupant" |
30 | 25 |
31 local default_history_length, max_history_length = 20, math.huge; | |
32 | 26 |
33 local is_kickable_error do | 27 local is_kickable_error do |
34 local kickable_error_conditions = { | 28 local kickable_error_conditions = { |
35 ["gone"] = true; | 29 ["gone"] = true; |
36 ["internal-server-error"] = true; | 30 ["internal-server-error"] = true; |
183 end | 177 end |
184 end | 178 end |
185 return x | 179 return x |
186 end | 180 end |
187 | 181 |
188 function room_mt:broadcast_message(stanza, historic) | 182 function room_mt:broadcast_message(stanza) |
189 module:fire_event("muc-broadcast-message", {room = self, stanza = stanza, historic = historic}); | 183 module:fire_event("muc-broadcast-message", {room = self, stanza = stanza}); |
190 self:broadcast(stanza); | 184 self:broadcast(stanza); |
191 end | 185 end |
192 | |
193 -- add to history | |
194 module:hook("muc-broadcast-message", function(event) | |
195 if event.historic then | |
196 local room = event.room | |
197 local history = room._data['history']; | |
198 if not history then history = {}; room._data['history'] = history; end | |
199 local stanza = st.clone(event.stanza); | |
200 stanza.attr.to = ""; | |
201 local ts = gettime(); | |
202 local stamp = datetime.datetime(ts); | |
203 stanza:tag("delay", {xmlns = "urn:xmpp:delay", from = module.host, stamp = stamp}):up(); -- XEP-0203 | |
204 stanza:tag("x", {xmlns = "jabber:x:delay", from = module.host, stamp = datetime.legacy()}):up(); -- XEP-0091 (deprecated) | |
205 local entry = { stanza = stanza, timestamp = ts }; | |
206 t_insert(history, entry); | |
207 while #history > room:get_historylength() do t_remove(history, 1) end | |
208 end | |
209 end); | |
210 | 186 |
211 -- Broadcast a stanza to all occupants in the room. | 187 -- Broadcast a stanza to all occupants in the room. |
212 -- optionally checks conditional called with (nick, occupant) | 188 -- optionally checks conditional called with (nick, occupant) |
213 function room_mt:broadcast(stanza, cond_func) | 189 function room_mt:broadcast(stanza, cond_func) |
214 for nick, occupant in self:each_occupant() do | 190 for nick, occupant in self:each_occupant() do |
310 self:route_stanza(pres); | 286 self:route_stanza(pres); |
311 end | 287 end |
312 end | 288 end |
313 end | 289 end |
314 | 290 |
315 local function parse_history(stanza) | |
316 local x_tag = stanza:get_child("x", "http://jabber.org/protocol/muc"); | |
317 local history_tag = x_tag and x_tag:get_child("history", "http://jabber.org/protocol/muc"); | |
318 if not history_tag then | |
319 return nil, 20, nil | |
320 end | |
321 | |
322 local maxchars = tonumber(history_tag.attr.maxchars); | |
323 | |
324 local maxstanzas = tonumber(history_tag.attr.maxstanzas); | |
325 | |
326 -- messages received since the UTC datetime specified | |
327 local since = history_tag.attr.since; | |
328 if since then | |
329 since = datetime.parse(since); | |
330 end | |
331 | |
332 -- messages received in the last "X" seconds. | |
333 local seconds = tonumber(history_tag.attr.seconds); | |
334 if seconds then | |
335 seconds = gettime() - seconds | |
336 if since then | |
337 since = math.max(since, seconds); | |
338 else | |
339 since = seconds; | |
340 end | |
341 end | |
342 | |
343 return maxchars, maxstanzas, since | |
344 end | |
345 | |
346 module:hook("muc-get-history", function(event) | |
347 local room = event.room | |
348 local history = room._data['history']; -- send discussion history | |
349 if not history then return nil end | |
350 local history_len = #history | |
351 | |
352 local to = event.to | |
353 local maxchars = event.maxchars | |
354 local maxstanzas = event.maxstanzas or history_len | |
355 local since = event.since | |
356 local n = 0; | |
357 local charcount = 0; | |
358 for i=history_len,1,-1 do | |
359 local entry = history[i]; | |
360 if maxchars then | |
361 if not entry.chars then | |
362 entry.stanza.attr.to = ""; | |
363 entry.chars = #tostring(entry.stanza); | |
364 end | |
365 charcount = charcount + entry.chars + #to; | |
366 if charcount > maxchars then break; end | |
367 end | |
368 if since and since > entry.timestamp then break; end | |
369 if n + 1 > maxstanzas then break; end | |
370 n = n + 1; | |
371 end | |
372 | |
373 local i = history_len-n+1 | |
374 function event:next_stanza() | |
375 if i > history_len then return nil end | |
376 local entry = history[i] | |
377 local msg = entry.stanza | |
378 msg.attr.to = to; | |
379 i = i + 1 | |
380 return msg | |
381 end | |
382 return true; | |
383 end); | |
384 | |
385 function room_mt:send_history(stanza) | |
386 local maxchars, maxstanzas, since = parse_history(stanza) | |
387 local event = { | |
388 room = self; | |
389 to = stanza.attr.from; -- `to` is required to calculate the character count for `maxchars` | |
390 maxchars = maxchars, maxstanzas = maxstanzas, since = since; | |
391 next_stanza = function() end; -- events should define this iterator | |
392 } | |
393 module:fire_event("muc-get-history", event) | |
394 for msg in event.next_stanza , event do | |
395 self:route_stanza(msg); | |
396 end | |
397 end | |
398 | |
399 function room_mt:get_disco_info(stanza) | 291 function room_mt:get_disco_info(stanza) |
400 local reply = st.reply(stanza):query("http://jabber.org/protocol/disco#info"); | 292 local reply = st.reply(stanza):query("http://jabber.org/protocol/disco#info"); |
401 local form = dataform.new { | 293 local form = dataform.new { |
402 { name = "FORM_TYPE", type = "hidden", value = "http://jabber.org/protocol/muc#roominfo" }; | 294 { name = "FORM_TYPE", type = "hidden", value = "http://jabber.org/protocol/muc#roominfo" }; |
403 }; | 295 }; |
449 if subject == "" then subject = nil; end | 341 if subject == "" then subject = nil; end |
450 self._data['subject'] = subject; | 342 self._data['subject'] = subject; |
451 self._data['subject_from'] = current_nick; | 343 self._data['subject_from'] = current_nick; |
452 if self.save then self:save(); end | 344 if self.save then self:save(); end |
453 local msg = create_subject_message(current_nick, subject); | 345 local msg = create_subject_message(current_nick, subject); |
454 self:broadcast_message(msg, false); | 346 self:broadcast_message(msg); |
455 return true; | 347 return true; |
456 end | 348 end |
457 | 349 |
458 function room_mt:handle_kickable(origin, stanza) | 350 function room_mt:handle_kickable(origin, stanza) |
459 local real_jid = stanza.attr.from; | 351 local real_jid = stanza.attr.from; |
527 end | 419 end |
528 end | 420 end |
529 function room_mt:get_changesubject() | 421 function room_mt:get_changesubject() |
530 return self._data.changesubject; | 422 return self._data.changesubject; |
531 end | 423 end |
532 function room_mt:get_historylength() | |
533 return self._data.history_length or default_history_length; | |
534 end | |
535 function room_mt:set_historylength(length) | |
536 length = math.min(tonumber(length) or default_history_length, max_history_length or math.huge); | |
537 if length == default_history_length then | |
538 length = nil; | |
539 end | |
540 self._data.history_length = length; | |
541 end | |
542 | 424 |
543 -- Give the room creator owner affiliation | 425 -- Give the room creator owner affiliation |
544 module:hook("muc-room-pre-create", function(event) | 426 module:hook("muc-room-pre-create", function(event) |
545 event.room:set_affiliation(true, jid_bare(event.stanza.attr.from), "owner"); | 427 event.room:set_affiliation(true, jid_bare(event.stanza.attr.from), "owner"); |
546 end, -1); | 428 end, -1); |
567 event.origin.send(reply:tag("x", {xmlns = "http://jabber.org/protocol/muc"})); | 449 event.origin.send(reply:tag("x", {xmlns = "http://jabber.org/protocol/muc"})); |
568 return true; | 450 return true; |
569 end | 451 end |
570 end, -10); | 452 end, -10); |
571 | 453 |
454 -- Send occupant list to newly joined user | |
572 module:hook("muc-occupant-joined", function(event) | 455 module:hook("muc-occupant-joined", function(event) |
573 local room, stanza = event.room, event.stanza; | 456 local real_jid = event.stanza.attr.from; |
574 local real_jid = stanza.attr.from; | 457 event.room:send_occupant_list(real_jid, function(nick, occupant) |
575 room:send_occupant_list(real_jid, function(nick, occupant) | |
576 -- Don't include self | 458 -- Don't include self |
577 return occupant:get_presence(real_jid) == nil; | 459 return occupant:get_presence(real_jid) == nil; |
578 end); | 460 end); |
579 room:send_history(stanza); | 461 end, 80); |
580 room:send_subject(real_jid); | 462 |
581 end, -1); | 463 -- Send subject to joining user |
464 module:hook("muc-occupant-joined", function(event) | |
465 event.room:send_subject(event.stanza.attr.from); | |
466 end, 20); | |
582 | 467 |
583 function room_mt:handle_presence_to_occupant(origin, stanza) | 468 function room_mt:handle_presence_to_occupant(origin, stanza) |
584 local type = stanza.attr.type; | 469 local type = stanza.attr.type; |
585 if type == "error" then -- error, kick em out! | 470 if type == "error" then -- error, kick em out! |
586 return self:handle_kickable(origin, stanza) | 471 return self:handle_kickable(origin, stanza) |
869 type = 'boolean', | 754 type = 'boolean', |
870 label = 'Make Room Members-Only?', | 755 label = 'Make Room Members-Only?', |
871 value = event.room:get_members_only() | 756 value = event.room:get_members_only() |
872 }); | 757 }); |
873 end); | 758 end); |
874 module:hook("muc-config-form", function(event) | |
875 table.insert(event.form, { | |
876 name = 'muc#roomconfig_historylength', | |
877 type = 'text-single', | |
878 label = 'Maximum Number of History Messages Returned by Room', | |
879 value = tostring(event.room:get_historylength()) | |
880 }); | |
881 end); | |
882 | 759 |
883 function room_mt:process_form(origin, stanza) | 760 function room_mt:process_form(origin, stanza) |
884 local form = stanza.tags[1]:get_child("x", "jabber:x:data"); | 761 local form = stanza.tags[1]:get_child("x", "jabber:x:data"); |
885 if form.attr.type == "cancel" then | 762 if form.attr.type == "cancel" then |
886 origin.send(st.reply(stanza)); | 763 origin.send(st.reply(stanza)); |
911 :tag('x', {xmlns='http://jabber.org/protocol/muc#user'}) | 788 :tag('x', {xmlns='http://jabber.org/protocol/muc#user'}) |
912 for code in pairs(event.status_codes) do | 789 for code in pairs(event.status_codes) do |
913 msg:tag("status", {code = code;}):up(); | 790 msg:tag("status", {code = code;}):up(); |
914 end | 791 end |
915 msg:up(); | 792 msg:up(); |
916 self:broadcast_message(msg, false) | 793 self:broadcast_message(msg); |
917 end | 794 end |
918 else | 795 else |
919 origin.send(st.error_reply(stanza, "cancel", "bad-request", "Not a submitted form")); | 796 origin.send(st.error_reply(stanza, "cancel", "bad-request", "Not a submitted form")); |
920 end | 797 end |
921 return true; | 798 return true; |
932 module:hook("muc-config-submitted", function(event) | 809 module:hook("muc-config-submitted", function(event) |
933 event.update_option("public", "muc#roomconfig_publicroom"); | 810 event.update_option("public", "muc#roomconfig_publicroom"); |
934 end); | 811 end); |
935 module:hook("muc-config-submitted", function(event) | 812 module:hook("muc-config-submitted", function(event) |
936 event.update_option("changesubject", "muc#roomconfig_changesubject"); | 813 event.update_option("changesubject", "muc#roomconfig_changesubject"); |
937 end); | |
938 module:hook("muc-config-submitted", function(event) | |
939 event.update_option("historylength", "muc#roomconfig_historylength"); | |
940 end); | 814 end); |
941 | 815 |
942 -- Removes everyone from the room | 816 -- Removes everyone from the room |
943 function room_mt:clear(x) | 817 function room_mt:clear(x) |
944 x = x or st.stanza("x", {xmlns='http://jabber.org/protocol/muc#user'}); | 818 x = x or st.stanza("x", {xmlns='http://jabber.org/protocol/muc#user'}); |
1097 else | 971 else |
1098 stanza.attr.from = from; | 972 stanza.attr.from = from; |
1099 origin.send(st.error_reply(stanza, "auth", "forbidden")); | 973 origin.send(st.error_reply(stanza, "auth", "forbidden")); |
1100 end | 974 end |
1101 else | 975 else |
1102 self:broadcast_message(stanza, self:get_historylength() > 0 and stanza:get_child("body")); | 976 self:broadcast_message(stanza); |
1103 end | 977 end |
1104 stanza.attr.from = from; | 978 stanza.attr.from = from; |
1105 return true; | 979 return true; |
1106 end | 980 end |
1107 end | 981 end |
1410 | 1284 |
1411 local whois = module:require "muc/whois"; | 1285 local whois = module:require "muc/whois"; |
1412 room_mt.get_whois = whois.get; | 1286 room_mt.get_whois = whois.get; |
1413 room_mt.set_whois = whois.set; | 1287 room_mt.set_whois = whois.set; |
1414 | 1288 |
1289 local history = module:require "muc/history"; | |
1290 room_mt.send_history = history.send; | |
1291 room_mt.get_historylength = history.get_length; | |
1292 room_mt.set_historylength = history.set_length; | |
1293 | |
1415 local _M = {}; -- module "muc" | 1294 local _M = {}; -- module "muc" |
1295 | |
1296 _M.set_max_history_length = history.set_max_length; | |
1416 | 1297 |
1417 function _M.new_room(jid, config) | 1298 function _M.new_room(jid, config) |
1418 return setmetatable({ | 1299 return setmetatable({ |
1419 jid = jid; | 1300 jid = jid; |
1420 _jid_nick = {}; | 1301 _jid_nick = {}; |
1421 _occupants = {}; | 1302 _occupants = {}; |
1422 _data = { | 1303 _data = { |
1423 history_length = math.min((config and config.history_length) | |
1424 or default_history_length, max_history_length); | |
1425 }; | 1304 }; |
1426 _affiliations = {}; | 1305 _affiliations = {}; |
1427 }, room_mt); | 1306 }, room_mt); |
1428 end | 1307 end |
1429 | 1308 |
1430 function _M.set_max_history_length(_max_history_length) | |
1431 max_history_length = _max_history_length or math.huge; | |
1432 end | |
1433 | |
1434 _M.room_mt = room_mt; | 1309 _M.room_mt = room_mt; |
1435 | 1310 |
1436 return _M; | 1311 return _M; |