Software / code / prosody-modules
Comparison
mod_groups_internal/mod_groups_internal.lua @ 5708: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 |
| parent | 4707:27f7ed9f50cd |
| child | 5818:5533c577dd02 |
comparison
equal
deleted
inserted
replaced
| 5707:7c264a2cb970 | 5708: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)"); |