Software /
code /
prosody-modules
Comparison
mod_groups_internal/mod_groups_internal.lua @ 5856:75dee6127829 draft
Merge upstream
author | Trần H. Trung <xmpp:trần.h.trung@trung.fun> |
---|---|
date | Tue, 06 Feb 2024 18:32:01 +0700 |
parent | 5848:865c77b5c6dc |
comparison
equal
deleted
inserted
replaced
5664:52db2da66680 | 5856:75dee6127829 |
---|---|
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; |
15 | 16 |
16 local is_contact_subscribed = rostermanager.is_contact_subscribed; | 17 local is_contact_subscribed = rostermanager.is_contact_subscribed; |
17 | 18 |
18 -- Make a *one-way* subscription. User will see when contact is online, | 19 -- Make a *one-way* subscription. User will see when contact is online, |
19 -- contact will not see when user is online. | 20 -- contact will not see when user is online. |
20 local function subscribe(user, user_jid, contact, contact_jid) | 21 local function subscribe(user, user_jid, contact, contact_jid, group_name) |
21 -- Update user's roster to say subscription request is pending... | 22 -- Update user's roster to say subscription request is pending... |
22 rostermanager.set_contact_pending_out(user, host, contact_jid); | 23 rostermanager.set_contact_pending_out(user, host, contact_jid); |
23 -- Update contact's roster to say subscription request is pending... | 24 -- Update contact's roster to say subscription request is pending... |
24 rostermanager.set_contact_pending_in(contact, host, user_jid); | 25 rostermanager.set_contact_pending_in(contact, host, user_jid); |
25 -- Update contact's roster to say subscription request approved... | 26 -- Update contact's roster to say subscription request approved... |
26 rostermanager.subscribed(contact, host, user_jid); | 27 rostermanager.subscribed(contact, host, user_jid); |
27 -- Update user's roster to say subscription request approved... | 28 -- Update user's roster to say subscription request approved... |
28 rostermanager.process_inbound_subscription_approval(user, host, contact_jid); | 29 rostermanager.process_inbound_subscription_approval(user, host, contact_jid); |
29 | 30 |
31 if group_name then | |
32 local user_roster = rostermanager.load_roster(user, host); | |
33 user_roster[contact_jid].groups[group_name] = true; | |
34 end | |
35 | |
30 -- Push updates to both rosters | 36 -- Push updates to both rosters |
31 rostermanager.roster_push(user, host, contact_jid); | 37 rostermanager.roster_push(user, host, contact_jid); |
32 rostermanager.roster_push(contact, host, user_jid); | 38 rostermanager.roster_push(contact, host, user_jid); |
33 end | 39 end |
34 | 40 |
37 end | 43 end |
38 | 44 |
39 local function do_single_group_subscriptions(username, group_id) | 45 local function do_single_group_subscriptions(username, group_id) |
40 local members = group_members_store:get(group_id); | 46 local members = group_members_store:get(group_id); |
41 if not members then return; end | 47 if not members then return; end |
48 local group_name = group_info_store:get_key(group_id, "name"); | |
42 local user_jid = jid_join(username, host); | 49 local user_jid = jid_join(username, host); |
43 for membername in pairs(members) do | 50 for membername in pairs(members) do |
44 if membername ~= username then | 51 if membername ~= username then |
45 local member_jid = jid_join(membername, host); | 52 local member_jid = jid_join(membername, host); |
46 if not is_contact_subscribed(username, host, member_jid) then | 53 if not is_contact_subscribed(username, host, member_jid) then |
47 module:log("debug", "[group %s] Subscribing %s to %s", member_jid, user_jid); | 54 module:log("debug", "[group %s] Subscribing %s to %s", member_jid, user_jid); |
48 subscribe(membername, member_jid, username, user_jid); | 55 subscribe(membername, member_jid, username, user_jid, group_name); |
49 end | 56 end |
50 if not is_contact_subscribed(membername, host, user_jid) then | 57 if not is_contact_subscribed(membername, host, user_jid) then |
51 module:log("debug", "[group %s] Subscribing %s to %s", user_jid, member_jid); | 58 module:log("debug", "[group %s] Subscribing %s to %s", user_jid, member_jid); |
52 subscribe(username, user_jid, membername, member_jid); | 59 subscribe(username, user_jid, membername, member_jid, group_name); |
53 end | 60 end |
54 end | 61 end |
55 end | 62 end |
56 end | 63 end |
57 | 64 |
74 module:hook("resource-bind", function(event) | 81 module:hook("resource-bind", function(event) |
75 module:log("debug", "Updating group subscriptions..."); | 82 module:log("debug", "Updating group subscriptions..."); |
76 do_all_group_subscriptions_by_user(event.session.username); | 83 do_all_group_subscriptions_by_user(event.session.username); |
77 end); | 84 end); |
78 | 85 |
86 local function _create_muc_room(name) | |
87 if not muc_host_name then | |
88 module:log("error", "cannot create group MUC: no MUC host configured") | |
89 return nil, "service-unavailable" | |
90 end | |
91 if not muc_host then | |
92 module:log("error", "cannot create group MUC: MUC host %s not configured properly", muc_host_name) | |
93 return nil, "internal-server-error" | |
94 end | |
95 | |
96 local muc_jid = jid.prep(id.short() .. "@" .. muc_host_name); | |
97 local room = muc_host.create_room(muc_jid) | |
98 if not room then | |
99 return nil, "internal-server-error" | |
100 end | |
101 | |
102 local ok = pcall(function () | |
103 room:set_public(false); | |
104 room:set_persistent(true); | |
105 room:set_members_only(true); | |
106 room:set_allow_member_invites(false); | |
107 room:set_moderated(false); | |
108 room:set_whois("anyone"); | |
109 room:set_name(name); | |
110 end); | |
111 | |
112 if not ok then | |
113 module:log("error", "Failed to configure group MUC %s", muc_jid); | |
114 room:destroy(); | |
115 return nil, "internal-server-error"; | |
116 end | |
117 | |
118 return muc_jid, room; | |
119 end | |
120 | |
79 --luacheck: ignore 131 | 121 --luacheck: ignore 131 |
80 function create(group_info, create_muc, group_id) | 122 function create(group_info, create_default_muc, group_id) |
81 if not group_info.name then | 123 if not group_info.name then |
82 return nil, "group-name-required"; | 124 return nil, "group-name-required"; |
83 end | 125 end |
84 if group_id then | 126 if group_id then |
85 if exists(group_id) then | 127 if exists(group_id) then |
89 group_id = id.short(); | 131 group_id = id.short(); |
90 end | 132 end |
91 | 133 |
92 local muc_jid = nil | 134 local muc_jid = nil |
93 local room = nil | 135 local room = nil |
94 if create_muc then | 136 if create_default_muc then |
95 if not muc_host_name then | 137 muc_jid, room = _create_muc_room(group_info.name); |
96 module:log("error", "cannot create group with MUC: no MUC host configured") | 138 if not muc_jid then |
97 return nil, "service-unavailable" | 139 -- 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) | 140 delete(group_id) |
108 return nil, "internal-server-error" | 141 return nil, room; |
109 end | 142 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 | 143 end |
118 | 144 |
119 local ok = group_info_store:set(group_id, { | 145 local ok = group_info_store:set(group_id, { |
120 name = group_info.name; | 146 name = group_info.name; |
121 muc_jid = muc_jid; | 147 muc_jid = muc_jid; |
156 end | 182 end |
157 return true | 183 return true |
158 end | 184 end |
159 | 185 |
160 function get_members(group_id) | 186 function get_members(group_id) |
161 return group_members_store:get(group_id); | 187 return group_members_store:get(group_id) or {}; |
162 end | 188 end |
163 | 189 |
164 function exists(group_id) | 190 function exists(group_id) |
165 return not not get_info(group_id); | 191 return not not get_info(group_id); |
166 end | 192 end |
198 return nil, "group-not-found"; | 224 return nil, "group-not-found"; |
199 end | 225 end |
200 if not group_memberships:set(group_id, username, {}) then | 226 if not group_memberships:set(group_id, username, {}) then |
201 return nil, "internal-server-error"; | 227 return nil, "internal-server-error"; |
202 end | 228 end |
229 | |
203 if group_info.muc_jid then | 230 if group_info.muc_jid then |
204 local room = muc_host.get_room_from_jid(group_info.muc_jid); | 231 local room = muc_host.get_room_from_jid(group_info.muc_jid); |
205 if room then | 232 if room then |
206 local user_jid = username .. "@" .. host; | 233 local user_jid = username .. "@" .. host; |
207 room:set_affiliation(true, user_jid, "member"); | 234 room:set_affiliation(true, user_jid, "member"); |
213 }):up()); | 240 }):up()); |
214 module:log("debug", "set user %s to be member in %s and sent invite", username, group_info.muc_jid); | 241 module:log("debug", "set user %s to be member in %s and sent invite", username, group_info.muc_jid); |
215 else | 242 else |
216 module:log("warn", "failed to update affiliation for %s in %s", username, group_info.muc_jid); | 243 module:log("warn", "failed to update affiliation for %s in %s", username, group_info.muc_jid); |
217 end | 244 end |
218 end | 245 elseif group_info.mucs then |
246 local user_jid = username .. "@" .. host; | |
247 for i = #group_info.mucs, 1, -1 do | |
248 local muc_jid = group_info.mucs[i]; | |
249 local room = muc_host.get_room_from_jid(muc_jid); | |
250 if not room or room._data.destroyed then | |
251 -- MUC no longer available, for some reason | |
252 -- Let's remove it from the circle metadata... | |
253 table.remove(group_info.mucs, i); | |
254 group_info_store:set_key(group_id, "mucs", group_info.mucs); | |
255 else | |
256 room:set_affiliation(true, user_jid, "member"); | |
257 module:send(st.message( | |
258 { from = muc_jid, to = user_jid } | |
259 ):tag("x", { | |
260 xmlns = "jabber:x:conference", | |
261 jid = muc_jid | |
262 }):up()); | |
263 module:log("debug", "set user %s to be member in %s and sent invite", username, muc_jid); | |
264 end | |
265 end | |
266 end | |
267 | |
219 module:fire_event( | 268 module:fire_event( |
220 "group-user-added", | 269 "group-user-added", |
221 { | 270 { |
222 id = group_id, | 271 id = group_id, |
223 user = username, | 272 user = username, |
245 local user_jid = username .. "@" .. host; | 294 local user_jid = username .. "@" .. host; |
246 room:set_affiliation(true, user_jid, nil); | 295 room:set_affiliation(true, user_jid, nil); |
247 else | 296 else |
248 module:log("warn", "failed to update affiliation for %s in %s", username, group_info.muc_jid); | 297 module:log("warn", "failed to update affiliation for %s in %s", username, group_info.muc_jid); |
249 end | 298 end |
250 end | 299 elseif group_info.mucs then |
300 local user_jid = username .. "@" .. host; | |
301 for _, muc_jid in ipairs(group_info.mucs) do | |
302 local room = muc_host.get_room_from_jid(muc_jid); | |
303 if room then | |
304 room:set_affiliation(true, user_jid, nil); | |
305 else | |
306 module:log("warn", "failed to update affiliation for %s in %s", username, muc_jid); | |
307 end | |
308 end | |
309 end | |
310 | |
251 module:fire_event( | 311 module:fire_event( |
252 "group-user-removed", | 312 "group-user-removed", |
253 { | 313 { |
254 id = group_id, | 314 id = group_id, |
255 user = username, | 315 user = username, |
262 | 322 |
263 function sync(group_id) | 323 function sync(group_id) |
264 do_all_group_subscriptions_by_group(group_id); | 324 do_all_group_subscriptions_by_group(group_id); |
265 end | 325 end |
266 | 326 |
327 function add_group_chat(group_id, name) | |
328 local group_info = group_info_store:get(group_id); | |
329 local mucs = group_info.mucs or {}; | |
330 | |
331 -- Create the MUC | |
332 local muc_jid, room = _create_muc_room(name); | |
333 if not muc_jid then return nil, room; end | |
334 room:save(); -- This ensures the room is committed to storage | |
335 | |
336 table.insert(mucs, muc_jid); | |
337 | |
338 if group_info.muc_jid then -- COMPAT include old muc_jid into array | |
339 table.insert(mucs, group_info.muc_jid); | |
340 end | |
341 local store_ok, store_err = group_info_store:set_key(group_id, "mucs", mucs); | |
342 if not store_ok then | |
343 module:log("error", "Failed to store new MUC association: %s", store_err); | |
344 room:destroy(); | |
345 return nil, "internal-server-error"; | |
346 end | |
347 | |
348 -- COMPAT: clear old muc_jid (it's now in mucs array) | |
349 if group_info.muc_jid then | |
350 module:log("debug", "Clearing old single-MUC JID"); | |
351 group_info.muc_jid = nil; | |
352 group_info_store:set_key(group_id, "muc_jid", nil); | |
353 end | |
354 | |
355 -- Make existing group members, members of the MUC | |
356 for username in pairs(get_members(group_id)) do | |
357 local user_jid = username .. "@" ..module.host; | |
358 room:set_affiliation(true, user_jid, "member"); | |
359 module:send(st.message( | |
360 { from = muc_jid, to = user_jid } | |
361 ):tag("x", { | |
362 xmlns = "jabber:x:conference", | |
363 jid = muc_jid | |
364 }):up()); | |
365 module:log("debug", "set user %s to be member in %s and sent invite", user_jid, muc_jid); | |
366 end | |
367 | |
368 -- Notify other modules (such as mod_groups_muc_bookmarks) | |
369 local muc = { | |
370 jid = muc_jid; | |
371 name = name; | |
372 }; | |
373 | |
374 module:fire_event("group-chat-added", { | |
375 group_id = group_id; | |
376 group_info = group_info; | |
377 muc = muc; | |
378 }); | |
379 | |
380 return muc; | |
381 end | |
382 | |
383 function remove_group_chat(group_id, muc_id) | |
384 local group_info = group_info_store:get(group_id); | |
385 if not group_info then | |
386 return nil, "group-not-found"; | |
387 end | |
388 | |
389 local mucs = group_info.mucs; | |
390 if not mucs then | |
391 if not group_info.muc_jid then | |
392 return true; | |
393 end | |
394 -- COMPAT with old single-MUC groups - upgrade to new format | |
395 mucs = {}; | |
396 end | |
397 if group_info.muc_jid then | |
398 table.insert(mucs, group_info.muc_jid); | |
399 end | |
400 | |
401 local removed; | |
402 for i, muc_jid in ipairs(mucs) do | |
403 if muc_id == jid.node(muc_jid) then | |
404 removed = table.remove(mucs, i); | |
405 break; | |
406 end | |
407 end | |
408 | |
409 if removed then | |
410 if not group_info_store:set_key(group_id, "mucs", mucs) then | |
411 return nil, "internal-server-error"; | |
412 end | |
413 | |
414 if group_info.muc_jid then | |
415 -- COMPAT: Now we've set the array, clean up muc_jid | |
416 group_info.muc_jid = nil; | |
417 group_info_store:set_key(group_id, "muc_jid", nil); | |
418 end | |
419 | |
420 module:log("debug", "Updated group MUC list"); | |
421 | |
422 local room = muc_host.get_room_from_jid(removed); | |
423 if room then | |
424 room:destroy(); | |
425 else | |
426 module:log("warn", "Removing a group chat, but associated MUC not found (%s)", removed); | |
427 end | |
428 | |
429 module:fire_event( | |
430 "group-chat-removed", | |
431 { | |
432 group_id = group_id; | |
433 group_info = group_info; | |
434 muc = { | |
435 id = muc_id; | |
436 jid = removed; | |
437 }; | |
438 } | |
439 ); | |
440 else | |
441 module:log("warn", "Removal of a group chat that can't be found - %s", muc_id); | |
442 end | |
443 | |
444 return true; | |
445 end | |
446 | |
447 function get_group_chats(group_id) | |
448 local group_info, err = group_info_store:get(group_id); | |
449 if not group_info then | |
450 module:log("debug", "Unable to load group info: %s - %s", group_id, err); | |
451 return nil; | |
452 end | |
453 | |
454 local mucs = group_info.mucs or {}; | |
455 | |
456 -- COMPAT with single-MUC groups | |
457 if group_info.muc_jid then | |
458 table.insert(mucs, group_info.muc_jid); | |
459 end | |
460 | |
461 return array.map(mucs, function (muc_jid) | |
462 local room = muc_host.get_room_from_jid(muc_jid); | |
463 return { | |
464 id = jid.node(muc_jid); | |
465 jid = muc_jid; | |
466 name = room and room:get_name() or group_info.name; | |
467 deleted = not room or room._data.destroyed; | |
468 }; | |
469 end); | |
470 end | |
471 | |
267 function emit_member_events(group_id) | 472 function emit_member_events(group_id) |
268 local group_info, err = get_info(group_id) | 473 local group_info, err = get_info(group_id) |
269 if group_info == nil then | 474 if group_info == nil then |
270 return false, err | 475 return false, err |
271 end | 476 end |
285 return true | 490 return true |
286 end | 491 end |
287 | 492 |
288 -- Returns iterator over group ids | 493 -- Returns iterator over group ids |
289 function groups() | 494 function groups() |
290 return group_info_store:users(); | 495 return group_info_store:items(); |
291 end | 496 end |
292 | 497 |
293 local function setup() | 498 local function setup() |
294 if not muc_host_name then | 499 if not muc_host_name then |
295 module:log("info", "MUC management disabled (groups_muc_host set to nil)"); | 500 module:log("info", "MUC management disabled (groups_muc_host set to nil)"); |