mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2025-12-30 11:22:31 +00:00
We were using prosody,util.json and cjson at the same time, but the latter is more performant. Adds some error handling which were missing with the prosody util json one.
212 lines
6.9 KiB
Lua
212 lines
6.9 KiB
Lua
-- This module provides persistence for the "polls" feature,
|
|
-- by keeping track of the state of polls in each room, and sending
|
|
-- that state to new participants when they join.
|
|
|
|
local json = require 'cjson.safe';
|
|
local st = require("util.stanza");
|
|
local jid = require "util.jid";
|
|
local util = module:require("util");
|
|
local muc = module:depends("muc");
|
|
|
|
local NS_NICK = 'http://jabber.org/protocol/nick';
|
|
local is_healthcheck_room = util.is_healthcheck_room;
|
|
|
|
-- Checks if the given stanza contains a JSON message,
|
|
-- and that the message type pertains to the polls feature.
|
|
-- If yes, returns the parsed message. Otherwise, returns nil.
|
|
local function get_poll_message(stanza)
|
|
if stanza.attr.type ~= "groupchat" then
|
|
return nil;
|
|
end
|
|
local json_data = stanza:get_child_text("json-message", "http://jitsi.org/jitmeet");
|
|
if json_data == nil then
|
|
return nil;
|
|
end
|
|
local data, error = json.decode(json_data);
|
|
if not data or (data.type ~= "new-poll" and data.type ~= "answer-poll") then
|
|
if error then
|
|
module:log('error', 'Error decoding data error:%s', error);
|
|
end
|
|
return nil;
|
|
end
|
|
return data;
|
|
end
|
|
|
|
-- Logs a warning and returns true if a room does not
|
|
-- have poll data associated with it.
|
|
local function check_polls(room)
|
|
if room.polls == nil then
|
|
module:log("warn", "no polls data in room");
|
|
return true;
|
|
end
|
|
return false;
|
|
end
|
|
|
|
--- Returns a table having occupant id and occupant name.
|
|
--- If the id cannot be extracted from nick a nil value is returned
|
|
--- if the occupant name cannot be extracted from presence the Fellow Jitster
|
|
--- name is used
|
|
local function get_occupant_details(occupant)
|
|
if not occupant then
|
|
return nil
|
|
end
|
|
local presence = occupant:get_presence();
|
|
local occupant_name;
|
|
if presence then
|
|
occupant_name = presence:get_child("nick", NS_NICK) and presence:get_child("nick", NS_NICK):get_text() or 'Fellow Jitster';
|
|
else
|
|
occupant_name = 'Fellow Jitster'
|
|
end
|
|
local _, _, occupant_id = jid.split(occupant.nick)
|
|
if not occupant_id then
|
|
return nil
|
|
end
|
|
return { ["occupant_id"] = occupant_id, ["occupant_name"] = occupant_name }
|
|
end
|
|
|
|
-- Sets up poll data in new rooms.
|
|
module:hook("muc-room-created", function(event)
|
|
local room = event.room;
|
|
if is_healthcheck_room(room.jid) then return end
|
|
module:log("debug", "setting up polls in room %s", room.jid);
|
|
room.polls = {
|
|
by_id = {};
|
|
order = {};
|
|
};
|
|
end);
|
|
|
|
-- Keeps track of the current state of the polls in each room,
|
|
-- by listening to "new-poll" and "answer-poll" messages,
|
|
-- and updating the room poll data accordingly.
|
|
-- This mirrors the client-side poll update logic.
|
|
module:hook("message/bare", function(event)
|
|
local data = get_poll_message(event.stanza);
|
|
if data == nil then return end
|
|
|
|
local room = muc.get_room_from_jid(event.stanza.attr.to);
|
|
|
|
if data.type == "new-poll" then
|
|
if check_polls(room) then return end
|
|
|
|
local occupant_jid = event.stanza.attr.from;
|
|
local occupant = room:get_occupant_by_real_jid(occupant_jid);
|
|
if not occupant then
|
|
module:log("error", "Occupant %s was not found in room %s", occupant_jid, room.jid)
|
|
return
|
|
end
|
|
local poll_creator = get_occupant_details(occupant)
|
|
if not poll_creator then
|
|
module:log("error", "Cannot retrieve poll creator id and name for %s from %s", occupant.jid, room.jid)
|
|
return
|
|
end
|
|
|
|
local answers = {}
|
|
local compact_answers = {}
|
|
for i, name in ipairs(data.answers) do
|
|
table.insert(answers, { name = name, voters = {} });
|
|
table.insert(compact_answers, { key = i, name = name});
|
|
end
|
|
|
|
local poll = {
|
|
id = data.pollId,
|
|
sender_id = poll_creator.occupant_id,
|
|
sender_name = poll_creator.occupant_name,
|
|
question = data.question,
|
|
answers = answers
|
|
};
|
|
|
|
room.polls.by_id[data.pollId] = poll
|
|
table.insert(room.polls.order, poll)
|
|
|
|
local pollData = {
|
|
event = event,
|
|
room = room,
|
|
poll = {
|
|
pollId = data.pollId,
|
|
senderId = poll_creator.occupant_id,
|
|
senderName = poll_creator.occupant_name,
|
|
question = data.question,
|
|
answers = compact_answers
|
|
}
|
|
}
|
|
module:fire_event("poll-created", pollData);
|
|
|
|
elseif data.type == "answer-poll" then
|
|
if check_polls(room) then return end
|
|
|
|
local occupant_jid = event.stanza.attr.from;
|
|
local occupant = room:get_occupant_by_real_jid(occupant_jid);
|
|
if not occupant then
|
|
module:log("error", "Occupant %s does not exists for room %s", occupant_jid, room.jid)
|
|
return
|
|
end
|
|
local poll = room.polls.by_id[data.pollId];
|
|
if poll == nil then
|
|
module:log("warn", "answering inexistent poll");
|
|
return;
|
|
end
|
|
|
|
local voter = get_occupant_details(occupant)
|
|
if not voter then
|
|
module:log("error", "Cannot retrieve voter id and name for %s from %s", occupant.jid, room.jid)
|
|
return
|
|
end
|
|
|
|
local answers = {};
|
|
for vote_option_idx, vote_flag in ipairs(data.answers) do
|
|
table.insert(answers, {
|
|
key = vote_option_idx,
|
|
value = vote_flag,
|
|
name = poll.answers[vote_option_idx].name,
|
|
});
|
|
poll.answers[vote_option_idx].voters[voter.occupant_id] = vote_flag and voter.occupant_name or nil;
|
|
end
|
|
local answerData = {
|
|
event = event,
|
|
room = room,
|
|
pollId = poll.id,
|
|
voterName = voter.occupant_name,
|
|
voterId = voter.occupant_id,
|
|
answers = answers
|
|
}
|
|
module:fire_event("answer-poll", answerData);
|
|
end
|
|
end);
|
|
|
|
-- Sends the current poll state to new occupants after joining a room.
|
|
module:hook("muc-occupant-joined", function(event)
|
|
local room = event.room;
|
|
if is_healthcheck_room(room.jid) then return end
|
|
if room.polls == nil or #room.polls.order == 0 then
|
|
return
|
|
end
|
|
|
|
local data = {
|
|
type = "old-polls",
|
|
polls = {},
|
|
};
|
|
for i, poll in ipairs(room.polls.order) do
|
|
data.polls[i] = {
|
|
id = poll.id,
|
|
senderId = poll.sender_id,
|
|
senderName = poll.sender_name,
|
|
question = poll.question,
|
|
answers = poll.answers
|
|
};
|
|
end
|
|
|
|
local json_msg_str, error = json.encode(data);
|
|
if not json_msg_str then
|
|
module:log('error', 'Error encoding data room:%s error:%s', room.jid, error);
|
|
end
|
|
|
|
local stanza = st.message({
|
|
from = room.jid,
|
|
to = event.occupant.jid
|
|
})
|
|
:tag("json-message", { xmlns = "http://jitsi.org/jitmeet" })
|
|
:text(json_msg_str)
|
|
:up();
|
|
room:route_stanza(stanza);
|
|
end);
|