comparison mod_groups_internal/mod_groups_internal.lua @ 5685:9edc698848e9

mod_groups_internal: Update to support multiple MUCs per group This was a feature request for Snikket.
author Matthew Wild <mwild1@gmail.com>
date Thu, 02 Nov 2023 16:59:44 +0000
parents 27f7ed9f50cd
children 5533c577dd02
comparison
equal deleted inserted replaced
5684:7c264a2cb970 5685:9edc698848e9
1 local rostermanager = require"core.rostermanager"; 1 local rostermanager = require"core.rostermanager";
2 local modulemanager = require"core.modulemanager"; 2 local modulemanager = require"core.modulemanager";
3 local array = require "util.array";
3 local id = require "util.id"; 4 local id = require "util.id";
4 local jid = require "util.jid"; 5 local jid = require "util.jid";
5 local st = require "util.stanza"; 6 local st = require "util.stanza";
6 local jid_join = jid.join; 7 local jid_join = jid.join;
7 local host = module.host; 8 local host = module.host;
8 9
9 local group_info_store = module:open_store("group_info"); 10 local group_info_store = module:open_store("group_info", "keyval+");
10 local group_members_store = module:open_store("groups"); 11 local group_members_store = module:open_store("groups");
11 local group_memberships = module:open_store("groups", "map"); 12 local group_memberships = module:open_store("groups", "map");
12 13
13 local muc_host_name = module:get_option("groups_muc_host", "groups."..host); 14 local muc_host_name = module:get_option("groups_muc_host", "groups."..host);
14 local muc_host = nil; 15 local muc_host = nil;
74 module:hook("resource-bind", function(event) 75 module:hook("resource-bind", function(event)
75 module:log("debug", "Updating group subscriptions..."); 76 module:log("debug", "Updating group subscriptions...");
76 do_all_group_subscriptions_by_user(event.session.username); 77 do_all_group_subscriptions_by_user(event.session.username);
77 end); 78 end);
78 79
80 local function _create_muc_room(name)
81 if not muc_host_name then
82 module:log("error", "cannot create group MUC: no MUC host configured")
83 return nil, "service-unavailable"
84 end
85 if not muc_host then
86 module:log("error", "cannot create group MUC: MUC host %s not configured properly", muc_host_name)
87 return nil, "internal-server-error"
88 end
89
90 local muc_jid = jid.prep(id.short() .. "@" .. muc_host_name);
91 local room = muc_host.create_room(muc_jid)
92 if not room then
93 return nil, "internal-server-error"
94 end
95
96 local ok = pcall(function ()
97 room:set_public(false);
98 room:set_persistent(true);
99 room:set_members_only(true);
100 room:set_allow_member_invites(false);
101 room:set_moderated(false);
102 room:set_whois("anyone");
103 room:set_name(name);
104 end);
105
106 if not ok then
107 module:log("error", "Failed to configure group MUC %s", muc_jid);
108 room:destroy();
109 return nil, "internal-server-error";
110 end
111
112 return muc_jid, room;
113 end
114
79 --luacheck: ignore 131 115 --luacheck: ignore 131
80 function create(group_info, create_muc, group_id) 116 function create(group_info, create_default_muc, group_id)
81 if not group_info.name then 117 if not group_info.name then
82 return nil, "group-name-required"; 118 return nil, "group-name-required";
83 end 119 end
84 if group_id then 120 if group_id then
85 if exists(group_id) then 121 if exists(group_id) then
89 group_id = id.short(); 125 group_id = id.short();
90 end 126 end
91 127
92 local muc_jid = nil 128 local muc_jid = nil
93 local room = nil 129 local room = nil
94 if create_muc then 130 if create_default_muc then
95 if not muc_host_name then 131 muc_jid, room = _create_muc_room(group_info.name);
96 module:log("error", "cannot create group with MUC: no MUC host configured") 132 if not muc_jid then
97 return nil, "service-unavailable" 133 -- MUC creation failed, fail to create group
98 end
99 if not muc_host then
100 module:log("error", "cannot create group with MUC: MUC host %s not configured properly", muc_host_name)
101 return nil, "internal-server-error"
102 end
103
104 muc_jid = jid.prep(id.short() .. "@" .. muc_host_name);
105 room = muc_host.create_room(muc_jid)
106 if not room then
107 delete(group_id) 134 delete(group_id)
108 return nil, "internal-server-error" 135 return nil, room;
109 end 136 end
110 room:set_public(false)
111 room:set_persistent(true)
112 room:set_members_only(true)
113 room:set_allow_member_invites(false)
114 room:set_moderated(false)
115 room:set_whois("anyone")
116 room:set_name(group_info.name)
117 end 137 end
118 138
119 local ok = group_info_store:set(group_id, { 139 local ok = group_info_store:set(group_id, {
120 name = group_info.name; 140 name = group_info.name;
121 muc_jid = muc_jid; 141 muc_jid = muc_jid;
156 end 176 end
157 return true 177 return true
158 end 178 end
159 179
160 function get_members(group_id) 180 function get_members(group_id)
161 return group_members_store:get(group_id); 181 return group_members_store:get(group_id) or {};
162 end 182 end
163 183
164 function exists(group_id) 184 function exists(group_id)
165 return not not get_info(group_id); 185 return not not get_info(group_id);
166 end 186 end
262 282
263 function sync(group_id) 283 function sync(group_id)
264 do_all_group_subscriptions_by_group(group_id); 284 do_all_group_subscriptions_by_group(group_id);
265 end 285 end
266 286
287 function add_group_chat(group_id, name)
288 local group_info = group_info_store:get(group_id);
289 local mucs = group_info.mucs or {};
290
291 -- Create the MUC
292 local muc_jid, room = _create_muc_room(name);
293 if not muc_jid then return nil, room; end
294 table.insert(mucs, muc_jid);
295 if group_info.muc_jid then -- COMPAT include old muc_jid into array
296 table.insert(mucs, group_info.muc_jid);
297 end
298 local store_ok, store_err = group_info_store:set_key(group_id, "mucs", mucs);
299 if not store_ok then
300 module:log("error", "Failed to store new MUC association: %s", store_err);
301 room:destroy();
302 return nil, "internal-server-error";
303 end
304
305 -- COMPAT: clear old muc_jid (it's now in mucs array)
306 if group_info.muc_jid then
307 module:log("debug", "Clearing old single-MUC JID");
308 group_info.muc_jid = nil;
309 group_info_store:set_key(group_id, "muc_jid", nil);
310 end
311
312 -- Make existing group members, members of the MUC
313 for username in pairs(get_members(group_id)) do
314 local user_jid = username .. "@" ..module.host;
315 room:set_affiliation(true, user_jid, "member");
316 module:send(st.message(
317 { from = muc_jid, to = user_jid }
318 ):tag("x", {
319 xmlns = "jabber:x:conference",
320 jid = muc_jid
321 }):up());
322 module:log("debug", "set user %s to be member in %s and sent invite", user_jid, muc_jid);
323 end
324
325 -- Notify other modules (such as mod_groups_muc_bookmarks)
326 local muc = {
327 jid = muc_jid;
328 name = name;
329 };
330
331 module:fire_event("group-chat-added", {
332 group_id = group_id;
333 group_info = group_info;
334 muc = muc;
335 });
336
337 return muc;
338 end
339
340 function remove_group_chat(group_id, muc_id)
341 local group_info = group_info_store:get(group_id);
342 if not group_info then
343 return nil, "group-not-found";
344 end
345
346 local mucs = group_info.mucs;
347 if not mucs then
348 if not group_info.muc_jid then
349 return true;
350 end
351 -- COMPAT with old single-MUC groups - upgrade to new format
352 mucs = {};
353 end
354 if group_info.muc_jid then
355 table.insert(mucs, group_info.muc_jid);
356 end
357
358 local removed;
359 for i, muc_jid in ipairs(mucs) do
360 if muc_id == jid.node(muc_jid) then
361 removed = table.remove(mucs, i);
362 break;
363 end
364 end
365
366 if removed then
367 if not group_info_store:set_key(group_id, "mucs", mucs) then
368 return nil, "internal-server-error";
369 end
370
371 if group_info.muc_jid then
372 -- COMPAT: Now we've set the array, clean up muc_jid
373 group_info.muc_jid = nil;
374 group_info_store:set_key(group_id, "muc_jid", nil);
375 end
376
377 module:log("debug", "Updated group MUC list");
378
379 local room = muc_host.get_room_from_jid(removed);
380 if room then
381 room:destroy();
382 else
383 module:log("warn", "Removing a group chat, but associated MUC not found (%s)", removed);
384 end
385
386 module:fire_event(
387 "group-chat-removed",
388 {
389 group_id = group_id;
390 group_info = group_info;
391 muc = {
392 id = muc_id;
393 jid = removed;
394 };
395 }
396 );
397 else
398 module:log("warn", "Removal of a group chat that can't be found - %s", muc_id);
399 end
400
401 return true;
402 end
403
404 function get_group_chats(group_id)
405 local group_info, err = group_info_store:get(group_id);
406 if not group_info then
407 module:log("debug", "Unable to load group info: %s - %s", group_id, err);
408 return nil;
409 end
410
411 local mucs = group_info.mucs or {};
412
413 -- COMPAT with single-MUC groups
414 if group_info.muc_jid then
415 table.insert(mucs, group_info.muc_jid);
416 end
417
418 return array.map(mucs, function (muc_jid)
419 return {
420 id = jid.node(muc_jid);
421 jid = muc_jid;
422 name = muc_host.get_room_from_jid(muc_jid):get_name();
423 };
424 end);
425 end
426
267 function emit_member_events(group_id) 427 function emit_member_events(group_id)
268 local group_info, err = get_info(group_id) 428 local group_info, err = get_info(group_id)
269 if group_info == nil then 429 if group_info == nil then
270 return false, err 430 return false, err
271 end 431 end
285 return true 445 return true
286 end 446 end
287 447
288 -- Returns iterator over group ids 448 -- Returns iterator over group ids
289 function groups() 449 function groups()
290 return group_info_store:users(); 450 return group_info_store:items();
291 end 451 end
292 452
293 local function setup() 453 local function setup()
294 if not muc_host_name then 454 if not muc_host_name then
295 module:log("info", "MUC management disabled (groups_muc_host set to nil)"); 455 module:log("info", "MUC management disabled (groups_muc_host set to nil)");