Browse Source

WIP: jibri queue component prosody modules

master
Aaron Van Meerten 5 years ago
parent
commit
b94c357cc2

+ 5
- 0
resources/prosody-plugins/mod_jibri_queue.lua View File

@@ -0,0 +1,5 @@
1
+local jibri_queue_component
2
+    = module:get_option_string(
3
+        "jibri_queue_component", "jibri_queue"..module.host);
4
+
5
+module:add_identity("component", "jibri_queue", jibri_queue_component);

+ 235
- 0
resources/prosody-plugins/mod_jibri_queue_component.lua View File

@@ -0,0 +1,235 @@
1
+local st = require "util.stanza";
2
+local jid = require "util.jid";
3
+local http = require "net.http";
4
+local json = require "cjson";
5
+local inspect = require('inspect');
6
+local socket = require "socket";
7
+local uuid_gen = require "util.uuid".generate;
8
+local jwt = require "luajwtjitsi";
9
+local it = require "util.iterators";
10
+
11
+local get_room_from_jid = module:require "util".get_room_from_jid;
12
+local room_jid_match_rewrite = module:require "util".room_jid_match_rewrite;
13
+local is_healthcheck_room = module:require "util".is_healthcheck_room;
14
+
15
+
16
+local ASAPKeyPath
17
+    = module:get_option_string("asap_key_path", '/etc/prosody/certs/asap.key');
18
+
19
+local ASAPKeyId
20
+    = module:get_option_string("asap_key_id", 'jitsi');
21
+
22
+local ASAPIssuer
23
+    = module:get_option_string("asap_issuer", 'jitsi');
24
+
25
+local ASAPAudience
26
+    = module:get_option_string("asap_audience", 'jibriqueue');
27
+
28
+local ASAPTTL
29
+    = module:get_option_number("asap_ttl", 3600);
30
+
31
+local ASAPTTL_THRESHOLD
32
+    = module:get_option_number("asap_ttl_threshold", 600);
33
+
34
+local ASAPKey;
35
+
36
+local http_headers = {
37
+    ["User-Agent"] = "Prosody ("..prosody.version.."; "..prosody.platform..")",
38
+    ["Content-Type"] = "application/json"
39
+};
40
+
41
+-- we use async to detect Prosody 0.10 and earlier
42
+local have_async = pcall(require, "util.async");
43
+if not have_async then
44
+    module:log("warn", "conference duration will not work with Prosody version 0.10 or less.");
45
+    return;
46
+end
47
+
48
+local muc_component_host = module:get_option_string("muc_component");
49
+if muc_component_host == nil then
50
+    log("error", "No muc_component specified. No muc to operate on for jibri queue!");
51
+    return;
52
+end
53
+
54
+log("info", "Starting jibri queue handling for %s", muc_component_host);
55
+
56
+-- Read ASAP key once on module startup
57
+local f = io.open(ASAPKeyPath, "r");
58
+if f then
59
+    ASAPKey = f:read("*all");
60
+    f:close();
61
+    if not ASAPKey then
62
+        module:log("warn", "No ASAP Key read, disabling muc_events plugin");
63
+        return
64
+    end
65
+else
66
+    module:log("warn", "Error reading ASAP Key, disabling muc_events plugin");
67
+    return
68
+end
69
+
70
+-- TODO: Figure out a less arbitrary default cache size.
71
+local jwtKeyCacheSize = module:get_option_number("jwt_pubkey_cache_size", 128);
72
+local jwtKeyCache = require"util.cache".new(jwtKeyCacheSize);
73
+
74
+local queueServiceURL
75
+    = module:get_option_string("jibri_queue_url");
76
+
77
+if queueServiceURL == nil then
78
+    log("error", "No jibri_queue_url specified. No service to contact!");
79
+    return;
80
+end
81
+
82
+local function round(num, numDecimalPlaces)
83
+    local mult = 10^(numDecimalPlaces or 0)
84
+    return math.floor(num * mult + 0.5) / mult
85
+end
86
+      
87
+local function generateToken(audience)
88
+    audience = audience or ASAPAudience
89
+    local t = os.time()
90
+    local err
91
+    local exp_key = 'asap_exp.'..audience
92
+    local token_key = 'asap_token.'..audience
93
+    local exp = jwtKeyCache:get(exp_key)
94
+    local token = jwtKeyCache:get(token_key)
95
+
96
+    --if we find a token and it isn't too far from expiry, then use it
97
+    if token ~= nil and exp ~= nil then
98
+        exp = tonumber(exp)
99
+        if (exp - t) > ASAPTTL_THRESHOLD then
100
+            return token
101
+        end
102
+    end
103
+
104
+    --expiry is the current time plus TTL
105
+    exp = t + ASAPTTL
106
+    local payload = {
107
+        iss = ASAPIssuer,
108
+        aud = audience,
109
+        nbf = t,
110
+        exp = exp,
111
+    }
112
+
113
+    -- encode
114
+    local alg = "RS256"
115
+    token, err = jwt.encode(payload, ASAPKey, alg, {kid = ASAPKeyId})
116
+    if not err then
117
+        token = 'Bearer '..token
118
+        jwtKeyCache:set(exp_key,exp)
119
+        jwtKeyCache:set(token_key,token)
120
+        return token
121
+    else
122
+        return ''
123
+    end
124
+end
125
+
126
+
127
+local function cb(content_, code_, response_, request_)
128
+    if code_ == 200 or code_ == 204 then
129
+        module:log("debug", "URL Callback: Code %s, Content %s, Request (host %s, path %s, body %s), Response: %s",
130
+                code_, content_, request_.host, request_.path, inspect(request_.body), inspect(response_));
131
+    else
132
+        module:log("warn", "URL Callback non successful: Code %s, Content %s, Request (%s), Response: %s",
133
+                code_, content_, inspect(request_), inspect(response_));
134
+    end
135
+end
136
+
137
+local function sendEvent(type,room_address,participant,edetails)
138
+    local event_ts = round(socket.gettime()*1000);
139
+    local out_event = {
140
+        ["conference"] = room_address,
141
+        ["event_type"] = "Event"..type,
142
+        ["participant"] = participant,
143
+        ["event_details"] = edetails,
144
+        ["event_ts"] = event_ts
145
+    }
146
+    module:log("debug","Sending event %s",inspect(out_event));
147
+
148
+    local headers = http_headers or {}
149
+    headers['Authorization'] = generateToken()
150
+
151
+    module:log("debug","Sending headers %s",inspect(headers));
152
+    local request = http.request(queueServiceURL, {
153
+        headers = headers,
154
+        method = "POST",
155
+        body = json.encode(out_event)
156
+    }, cb);
157
+end
158
+
159
+-- receives messages from client currently connected to the room
160
+-- clients indicates their own dominant speaker events
161
+function on_message(event)
162
+    -- Check the type of the incoming stanza to avoid loops:
163
+    if event.stanza.attr.type == "error" then
164
+        return; -- We do not want to reply to these, so leave.
165
+    end
166
+
167
+    local jibriQueue
168
+        = jibriQueue.stanza:get_child('jibriqueue', 'http://jitsi.org/jitmeet');
169
+    if jibriQueue then
170
+        local roomAddress = jibriQueue.attr.room;
171
+        local room = get_room_from_jid(room_jid_match_rewrite(roomAddress));
172
+
173
+        if not room then
174
+            log("warn", "No room found %s", roomAddress);
175
+            return false;
176
+        end
177
+
178
+        local from = event.stanza.attr.from;
179
+
180
+        local occupant = room:get_occupant_by_real_jid(from);
181
+        if not occupant then
182
+            log("warn", "No occupant %s found for %s", from, roomAddress);
183
+            return false;
184
+        end
185
+        -- now handle new jibri queue message
186
+        local edetails = {
187
+            ["foo"] = "bar"
188
+        }
189
+        sendEvent('Message',roomAddress,from,edetails)
190
+    end
191
+    return true
192
+end
193
+
194
+function occupant_joined(event)
195
+    local room = event.room;
196
+    local occupant = event.occupant;
197
+
198
+    if is_healthcheck_room(room.jid) then
199
+        return;
200
+    end
201
+
202
+    local participant_count = it.count(room:each_occupant());
203
+
204
+    -- now handle new jibri queue message
205
+    local edetails = {
206
+        ["participant_count"] = participant_count
207
+    }
208
+    sendEvent('Join',room.jid,occupant.jid,edetails)
209
+end
210
+
211
+module:hook("message/host", on_message);
212
+
213
+-- executed on every host added internally in prosody, including components
214
+function process_host(host)
215
+    if host == muc_component_host then -- the conference muc component
216
+        module:log("info","Hook to muc events on %s", host);
217
+
218
+        local muc_module = module:context(host);
219
+        -- muc_module:hook("muc-room-created", room_created, -1);
220
+        muc_module:hook("muc-occupant-joined", occupant_joined, -1);
221
+        -- muc_module:hook("muc-occupant-pre-leave", occupant_leaving, -1);
222
+        -- muc_module:hook("muc-room-destroyed", room_destroyed, -1);
223
+    end
224
+end
225
+
226
+if prosody.hosts[muc_component_host] == nil then
227
+    module:log("info","No muc component found, will listen for it: %s", muc_component_host)
228
+
229
+    -- when a host or component is added
230
+    prosody.events.add_handler("host-activated", process_host);
231
+else
232
+    process_host(muc_component_host);
233
+end
234
+
235
+module:log("info", "Loading jibri_queue_component");

Loading…
Cancel
Save