Mercurial > prosody-modules
annotate mod_muc_cloud_notify/mod_muc_cloud_notify.lua @ 3866:c0df50ce96f0
mod_rest: Handle internal http request errors early and then return
Skips over attempted parsing of the payload which usually failed since
the body is an error string like "connection refused", so this produced
useless errors.
| author | Kim Alvefur <zash@zash.se> |
|---|---|
| date | Sat, 25 Jan 2020 20:22:12 +0100 |
| parents | 8a93af85f319 |
| children | 3b8f4f3b1718 |
| rev | line source |
|---|---|
| 3319 | 1 -- XEP-XXX: MUC Push Notifications |
| 2 -- Copyright (C) 2015-2016 Kim Alvefur | |
| 3 -- Copyright (C) 2017-2018 Thilo Molitor | |
| 4 -- | |
| 5 -- This file is MIT/X11 licensed. | |
| 6 | |
| 7 local s_match = string.match; | |
| 8 local s_sub = string.sub; | |
| 9 local os_time = os.time; | |
| 10 local next = next; | |
| 11 local st = require"util.stanza"; | |
| 12 local jid = require"util.jid"; | |
| 13 local dataform = require"util.dataforms".new; | |
| 14 local hashes = require"util.hashes"; | |
| 15 | |
| 16 local xmlns_push = "urn:xmpp:push:0"; | |
| 17 | |
| 18 -- configuration | |
| 19 local include_body = module:get_option_boolean("push_notification_with_body", false); | |
| 20 local include_sender = module:get_option_boolean("push_notification_with_sender", false); | |
| 21 local max_push_errors = module:get_option_number("push_max_errors", 16); | |
| 22 local max_push_devices = module:get_option_number("push_max_devices", 5); | |
| 23 local dummy_body = module:get_option_string("push_notification_important_body", "New Message!"); | |
| 24 | |
| 25 local host_sessions = prosody.hosts[module.host].sessions; | |
| 26 local push_errors = {}; | |
| 27 local id2node = {}; | |
| 28 | |
| 29 module:depends("muc"); | |
| 30 | |
| 31 -- ordered table iterator, allow to iterate on the natural order of the keys of a table, | |
| 32 -- see http://lua-users.org/wiki/SortedIteration | |
| 33 local function __genOrderedIndex( t ) | |
| 34 local orderedIndex = {} | |
| 35 for key in pairs(t) do | |
| 36 table.insert( orderedIndex, key ) | |
| 37 end | |
| 38 -- sort in reverse order (newest one first) | |
| 39 table.sort( orderedIndex, function(a, b) | |
| 40 if a == nil or t[a] == nil or b == nil or t[b] == nil then return false end | |
| 41 -- only one timestamp given, this is the newer one | |
| 42 if t[a].timestamp ~= nil and t[b].timestamp == nil then return true end | |
| 43 if t[a].timestamp == nil and t[b].timestamp ~= nil then return false end | |
| 44 -- both timestamps given, sort normally | |
| 45 if t[a].timestamp ~= nil and t[b].timestamp ~= nil then return t[a].timestamp > t[b].timestamp end | |
| 46 return false -- normally not reached | |
| 47 end) | |
| 48 return orderedIndex | |
| 49 end | |
| 50 local function orderedNext(t, state) | |
| 51 -- Equivalent of the next function, but returns the keys in timestamp | |
| 52 -- order. We use a temporary ordered key table that is stored in the | |
| 53 -- table being iterated. | |
| 54 | |
| 55 local key = nil | |
| 56 --print("orderedNext: state = "..tostring(state) ) | |
| 57 if state == nil then | |
| 58 -- the first time, generate the index | |
| 59 t.__orderedIndex = __genOrderedIndex( t ) | |
| 60 key = t.__orderedIndex[1] | |
| 61 else | |
| 62 -- fetch the next value | |
| 63 for i = 1, #t.__orderedIndex do | |
| 64 if t.__orderedIndex[i] == state then | |
| 65 key = t.__orderedIndex[i+1] | |
| 66 end | |
| 67 end | |
| 68 end | |
| 69 | |
| 70 if key then | |
| 71 return key, t[key] | |
| 72 end | |
| 73 | |
| 74 -- no more value to return, cleanup | |
| 75 t.__orderedIndex = nil | |
| 76 return | |
| 77 end | |
| 78 local function orderedPairs(t) | |
| 79 -- Equivalent of the pairs() function on tables. Allows to iterate | |
| 80 -- in order | |
| 81 return orderedNext, t, nil | |
| 82 end | |
| 83 | |
| 84 -- small helper function to return new table with only "maximum" elements containing only the newest entries | |
| 85 local function reduce_table(table, maximum) | |
| 86 local count = 0; | |
| 87 local result = {}; | |
| 88 for key, value in orderedPairs(table) do | |
| 89 count = count + 1; | |
| 90 if count > maximum then break end | |
| 91 result[key] = value; | |
| 92 end | |
| 93 return result; | |
| 94 end | |
| 95 | |
| 96 -- For keeping state across reloads while caching reads | |
| 97 local push_store = (function() | |
| 98 local store = module:open_store(); | |
| 99 local push_services = {}; | |
| 100 local api = {}; | |
| 101 function api:get(user) | |
| 102 if not push_services[user] then | |
| 103 local err; | |
| 104 push_services[user], err = store:get(user); | |
| 105 if not push_services[user] and err then | |
| 106 module:log("warn", "Error reading push notification storage for user '%s': %s", user, tostring(err)); | |
| 107 push_services[user] = {}; | |
| 108 return push_services[user], false; | |
| 109 end | |
| 110 end | |
| 111 if not push_services[user] then push_services[user] = {} end | |
| 112 return push_services[user], true; | |
| 113 end | |
| 114 function api:set(user, data) | |
| 115 push_services[user] = reduce_table(data, max_push_devices); | |
| 116 local ok, err = store:set(user, push_services[user]); | |
| 117 if not ok then | |
| 118 module:log("error", "Error writing push notification storage for user '%s': %s", user, tostring(err)); | |
| 119 return false; | |
| 120 end | |
| 121 return true; | |
| 122 end | |
| 123 function api:set_identifier(user, push_identifier, data) | |
| 124 local services = self:get(user); | |
| 125 services[push_identifier] = data; | |
| 126 return self:set(user, services); | |
| 127 end | |
| 128 return api; | |
| 129 end)(); | |
| 130 | |
| 131 | |
| 132 -- Forward declarations, as both functions need to reference each other | |
| 133 local handle_push_success, handle_push_error; | |
| 134 | |
| 135 function handle_push_error(event) | |
| 136 local stanza = event.stanza; | |
| 137 local error_type, condition = stanza:get_error(); | |
| 138 local node = id2node[stanza.attr.id]; | |
| 139 if node == nil then return false; end -- unknown stanza? Ignore for now! | |
| 140 local from = stanza.attr.from; | |
| 141 local user_push_services = push_store:get(node); | |
| 142 local changed = false; | |
| 143 | |
| 144 for push_identifier, _ in pairs(user_push_services) do | |
| 145 local stanza_id = hashes.sha256(push_identifier, true); | |
| 146 if stanza_id == stanza.attr.id then | |
| 147 if user_push_services[push_identifier] and user_push_services[push_identifier].jid == from and error_type ~= "wait" then | |
| 148 push_errors[push_identifier] = push_errors[push_identifier] + 1; | |
| 149 module:log("info", "Got error of type '%s' (%s) for identifier '%s': " | |
| 150 .."error count for this identifier is now at %s", error_type, condition, push_identifier, | |
| 151 tostring(push_errors[push_identifier])); | |
| 152 if push_errors[push_identifier] >= max_push_errors then | |
| 153 module:log("warn", "Disabling push notifications for identifier '%s'", push_identifier); | |
| 154 -- remove push settings from sessions | |
| 155 if host_sessions[node] then | |
| 156 for _, session in pairs(host_sessions[node].sessions) do | |
| 157 if session.push_identifier == push_identifier then | |
| 158 session.push_identifier = nil; | |
| 159 session.push_settings = nil; | |
| 160 session.first_hibernated_push = nil; | |
| 161 end | |
| 162 end | |
| 163 end | |
| 164 -- save changed global config | |
| 165 changed = true; | |
| 166 user_push_services[push_identifier] = nil | |
| 167 push_errors[push_identifier] = nil; | |
| 168 -- unhook iq handlers for this identifier (if possible) | |
| 169 if module.unhook then | |
| 170 module:unhook("iq-error/host/"..stanza_id, handle_push_error); | |
| 171 module:unhook("iq-result/host/"..stanza_id, handle_push_success); | |
| 172 id2node[stanza_id] = nil; | |
| 173 end | |
| 174 end | |
| 175 elseif user_push_services[push_identifier] and user_push_services[push_identifier].jid == from and error_type == "wait" then | |
| 176 module:log("debug", "Got error of type '%s' (%s) for identifier '%s': " | |
| 177 .."NOT increasing error count for this identifier", error_type, condition, push_identifier); | |
| 178 end | |
| 179 end | |
| 180 end | |
| 181 if changed then | |
| 182 push_store:set(node, user_push_services); | |
| 183 end | |
| 184 return true; | |
| 185 end | |
| 186 | |
| 187 function handle_push_success(event) | |
| 188 local stanza = event.stanza; | |
| 189 local node = id2node[stanza.attr.id]; | |
| 190 if node == nil then return false; end -- unknown stanza? Ignore for now! | |
| 191 local from = stanza.attr.from; | |
| 192 local user_push_services = push_store:get(node); | |
| 193 | |
| 194 for push_identifier, _ in pairs(user_push_services) do | |
| 195 if hashes.sha256(push_identifier, true) == stanza.attr.id then | |
| 196 if user_push_services[push_identifier] and user_push_services[push_identifier].jid == from and push_errors[push_identifier] > 0 then | |
| 197 push_errors[push_identifier] = 0; | |
| 198 module:log("debug", "Push succeeded, error count for identifier '%s' is now at %s again", push_identifier, tostring(push_errors[push_identifier])); | |
| 199 end | |
| 200 end | |
| 201 end | |
| 202 return true; | |
| 203 end | |
| 204 | |
| 205 -- http://xmpp.org/extensions/xep-0357.html#disco | |
| 206 local function account_dico_info(event) | |
| 207 (event.reply or event.stanza):tag("feature", {var=xmlns_push}):up(); | |
| 208 end | |
| 209 module:hook("account-disco-info", account_dico_info); | |
| 210 | |
| 211 -- http://xmpp.org/extensions/xep-0357.html#enabling | |
| 212 local function push_enable(event) | |
| 213 local origin, stanza = event.origin, event.stanza; | |
| 214 local enable = stanza.tags[1]; | |
| 215 origin.log("debug", "Attempting to enable push notifications"); | |
| 216 -- MUST contain a 'jid' attribute of the XMPP Push Service being enabled | |
| 217 local push_jid = enable.attr.jid; | |
| 218 -- SHOULD contain a 'node' attribute | |
| 219 local push_node = enable.attr.node; | |
| 220 -- CAN contain a 'include_payload' attribute | |
| 221 local include_payload = enable.attr.include_payload; | |
| 222 if not push_jid then | |
| 223 origin.log("debug", "MUC Push notification enable request missing the 'jid' field"); | |
| 224 origin.send(st.error_reply(stanza, "modify", "bad-request", "Missing jid")); | |
| 225 return true; | |
| 226 end | |
| 227 local publish_options = enable:get_child("x", "jabber:x:data"); | |
| 228 if not publish_options then | |
| 229 -- Could be intentional | |
| 230 origin.log("debug", "No publish options in request"); | |
| 231 end | |
| 232 local push_identifier = push_jid .. "<" .. (push_node or ""); | |
| 233 local push_service = { | |
| 234 jid = push_jid; | |
| 235 node = push_node; | |
| 236 include_payload = include_payload; | |
| 237 options = publish_options and st.preserialize(publish_options); | |
| 238 timestamp = os_time(); | |
| 239 }; | |
| 240 local ok = push_store:set_identifier(origin.username.."@"..origin.host, push_identifier, push_service); | |
| 241 if not ok then | |
| 242 origin.send(st.error_reply(stanza, "wait", "internal-server-error")); | |
| 243 else | |
| 244 origin.push_identifier = push_identifier; | |
| 245 origin.push_settings = push_service; | |
| 246 origin.first_hibernated_push = nil; | |
| 247 origin.log("info", "MUC Push notifications enabled for %s by %s (%s)", | |
| 248 tostring(stanza.attr.to), | |
| 249 tostring(stanza.attr.from), | |
| 250 tostring(origin.push_identifier) | |
| 251 ); | |
| 252 origin.send(st.reply(stanza)); | |
| 253 end | |
| 254 return true; | |
| 255 end | |
| 256 module:hook("iq-set/host/"..xmlns_push..":enable", push_enable); | |
| 257 | |
| 258 | |
| 259 -- http://xmpp.org/extensions/xep-0357.html#disabling | |
| 260 local function push_disable(event) | |
| 261 local origin, stanza = event.origin, event.stanza; | |
| 262 local push_jid = stanza.tags[1].attr.jid; -- MUST include a 'jid' attribute | |
| 263 local push_node = stanza.tags[1].attr.node; -- A 'node' attribute MAY be included | |
| 264 if not push_jid then | |
| 265 origin.send(st.error_reply(stanza, "modify", "bad-request", "Missing jid")); | |
| 266 return true; | |
| 267 end | |
| 268 local user_push_services = push_store:get(origin.username); | |
| 269 for key, push_info in pairs(user_push_services) do | |
| 270 if push_info.jid == push_jid and (not push_node or push_info.node == push_node) then | |
| 271 origin.log("info", "Push notifications disabled (%s)", tostring(key)); | |
| 272 if origin.push_identifier == key then | |
| 273 origin.push_identifier = nil; | |
| 274 origin.push_settings = nil; | |
| 275 origin.first_hibernated_push = nil; | |
| 276 end | |
| 277 user_push_services[key] = nil; | |
| 278 push_errors[key] = nil; | |
| 279 if module.unhook then | |
| 280 module:unhook("iq-error/host/"..key, handle_push_error); | |
| 281 module:unhook("iq-result/host/"..key, handle_push_success); | |
| 282 id2node[key] = nil; | |
| 283 end | |
| 284 end | |
| 285 end | |
| 286 local ok = push_store:set(origin.username, user_push_services); | |
| 287 if not ok then | |
| 288 origin.send(st.error_reply(stanza, "wait", "internal-server-error")); | |
| 289 else | |
| 290 origin.send(st.reply(stanza)); | |
| 291 end | |
| 292 return true; | |
| 293 end | |
| 294 module:hook("iq-set/host/"..xmlns_push..":disable", push_disable); | |
| 295 | |
| 296 -- Patched version of util.stanza:find() that supports giving stanza names | |
| 297 -- without their namespace, allowing for every namespace. | |
| 298 local function find(self, path) | |
| 299 local pos = 1; | |
| 300 local len = #path + 1; | |
| 301 | |
| 302 repeat | |
| 303 local xmlns, name, text; | |
| 304 local char = s_sub(path, pos, pos); | |
| 305 if char == "@" then | |
| 306 return self.attr[s_sub(path, pos + 1)]; | |
| 307 elseif char == "{" then | |
| 308 xmlns, pos = s_match(path, "^([^}]+)}()", pos + 1); | |
| 309 end | |
| 310 name, text, pos = s_match(path, "^([^@/#]*)([/#]?)()", pos); | |
| 311 name = name ~= "" and name or nil; | |
| 312 if pos == len then | |
| 313 if text == "#" then | |
| 314 local child = xmlns ~= nil and self:get_child(name, xmlns) or self:child_with_name(name); | |
| 315 return child and child:get_text() or nil; | |
| 316 end | |
| 317 return xmlns ~= nil and self:get_child(name, xmlns) or self:child_with_name(name); | |
| 318 end | |
| 319 self = xmlns ~= nil and self:get_child(name, xmlns) or self:child_with_name(name); | |
| 320 until not self | |
| 321 return nil; | |
| 322 end | |
| 323 | |
| 324 -- is this push a high priority one (this is needed for ios apps not using voip pushes) | |
| 325 local function is_important(stanza) | |
| 326 local st_name = stanza and stanza.name or nil; | |
| 327 if not st_name then return false; end -- nonzas are never important here | |
| 328 if st_name == "presence" then | |
| 329 return false; -- same for presences | |
| 330 elseif st_name == "message" then | |
| 331 -- unpack carbon copies | |
| 332 local stanza_direction = "in"; | |
| 333 local carbon; | |
| 334 local st_type; | |
| 335 -- support carbon copied message stanzas having an arbitrary message-namespace or no message-namespace at all | |
| 336 if not carbon then carbon = find(stanza, "{urn:xmpp:carbons:2}/forwarded/message"); end | |
| 337 if not carbon then carbon = find(stanza, "{urn:xmpp:carbons:1}/forwarded/message"); end | |
| 338 stanza_direction = carbon and stanza:child_with_name("sent") and "out" or "in"; | |
| 339 if carbon then stanza = carbon; end | |
| 340 st_type = stanza.attr.type; | |
| 341 | |
| 342 -- headline message are always not important | |
| 343 if st_type == "headline" then return false; end | |
| 344 | |
| 345 -- carbon copied outgoing messages are not important | |
| 346 if carbon and stanza_direction == "out" then return false; end | |
| 347 | |
| 348 -- We can't check for body contents in encrypted messages, so let's treat them as important | |
| 349 -- Some clients don't even set a body or an empty body for encrypted messages | |
| 350 | |
| 351 -- check omemo https://xmpp.org/extensions/inbox/omemo.html | |
| 352 if stanza:get_child("encrypted", "eu.siacs.conversations.axolotl") or stanza:get_child("encrypted", "urn:xmpp:omemo:0") then return true; end | |
| 353 | |
| 354 -- check xep27 pgp https://xmpp.org/extensions/xep-0027.html | |
| 355 if stanza:get_child("x", "jabber:x:encrypted") then return true; end | |
| 356 | |
| 357 -- check xep373 pgp (OX) https://xmpp.org/extensions/xep-0373.html | |
| 358 if stanza:get_child("openpgp", "urn:xmpp:openpgp:0") then return true; end | |
| 359 | |
| 360 local body = stanza:get_child_text("body"); | |
| 361 if st_type == "groupchat" and stanza:get_child_text("subject") then return false; end -- groupchat subjects are not important here | |
| 362 return body ~= nil and body ~= ""; -- empty bodies are not important | |
| 363 end | |
| 364 return false; -- this stanza wasn't one of the above cases --> it is not important, too | |
| 365 end | |
| 366 | |
| 367 local push_form = dataform { | |
| 368 { name = "FORM_TYPE"; type = "hidden"; value = "urn:xmpp:push:summary"; }; | |
| 369 { name = "message-count"; type = "text-single"; }; | |
| 370 { name = "pending-subscription-count"; type = "text-single"; }; | |
| 371 { name = "last-message-sender"; type = "jid-single"; }; | |
| 372 { name = "last-message-body"; type = "text-single"; }; | |
| 373 }; | |
| 374 | |
| 375 -- http://xmpp.org/extensions/xep-0357.html#publishing | |
| 376 local function handle_notify_request(stanza, node, user_push_services, log_push_decline) | |
| 377 local pushes = 0; | |
| 378 if not user_push_services or next(user_push_services) == nil then return pushes end | |
| 379 | |
| 380 -- XXX: customized | |
| 381 local body = stanza:get_child_text("body"); | |
| 382 if not body then | |
| 383 return pushes; | |
| 384 end | |
| 385 | |
| 386 for push_identifier, push_info in pairs(user_push_services) do | |
| 387 local send_push = true; -- only send push to this node when not already done for this stanza or if no stanza is given at all | |
| 388 if stanza then | |
| 389 if not stanza._push_notify then stanza._push_notify = {}; end | |
| 390 if stanza._push_notify[push_identifier] then | |
| 391 if log_push_decline then | |
| 392 module:log("debug", "Already sent push notification for %s@%s to %s (%s)", node, module.host, push_info.jid, tostring(push_info.node)); | |
| 393 end | |
| 394 send_push = false; | |
| 395 end | |
| 396 stanza._push_notify[push_identifier] = true; | |
| 397 end | |
| 398 | |
| 399 if send_push then | |
| 400 -- construct push stanza | |
| 401 local stanza_id = hashes.sha256(push_identifier, true); | |
| 402 local push_publish = st.iq({ to = push_info.jid, from = module.host, type = "set", id = stanza_id }) | |
| 403 :tag("pubsub", { xmlns = "http://jabber.org/protocol/pubsub" }) | |
| 404 :tag("publish", { node = push_info.node }) | |
| 405 :tag("item") | |
| 406 :tag("notification", { xmlns = xmlns_push }); | |
| 407 local form_data = { | |
| 408 -- hardcode to 1 because other numbers are just meaningless (the XEP does not specify *what exactly* to count) | |
| 409 ["message-count"] = "1"; | |
| 410 }; | |
| 411 if stanza and include_sender then | |
| 412 form_data["last-message-sender"] = stanza.attr.from; | |
| 413 end | |
| 414 if stanza and include_body then | |
| 415 form_data["last-message-body"] = stanza:get_child_text("body"); | |
| 416 elseif stanza and dummy_body and is_important(stanza) then | |
| 417 form_data["last-message-body"] = tostring(dummy_body); | |
| 418 end | |
| 419 push_publish:add_child(push_form:form(form_data)); | |
| 420 push_publish:up(); -- / notification | |
| 421 push_publish:up(); -- / publish | |
| 422 push_publish:up(); -- / pubsub | |
| 423 if push_info.options then | |
| 424 push_publish:tag("publish-options"):add_child(st.deserialize(push_info.options)); | |
| 425 end | |
| 426 -- send out push | |
| 427 module:log("debug", "Sending%s push notification for %s@%s to %s (%s)", form_data["last-message-body"] and " important" or "", node, module.host, push_info.jid, tostring(push_info.node)); | |
| 428 -- module:log("debug", "PUSH STANZA: %s", tostring(push_publish)); | |
| 429 -- handle push errors for this node | |
| 430 if push_errors[push_identifier] == nil then | |
| 431 push_errors[push_identifier] = 0; | |
| 432 module:hook("iq-error/host/"..stanza_id, handle_push_error); | |
| 433 module:hook("iq-result/host/"..stanza_id, handle_push_success); | |
| 434 id2node[stanza_id] = node; | |
| 435 end | |
| 436 module:send(push_publish); | |
| 437 pushes = pushes + 1; | |
| 438 end | |
| 439 end | |
| 440 return pushes; | |
| 441 end | |
| 442 | |
|
3570
8a93af85f319
mod_muc_cloud_notify: Count codepoints instead of bytes
Matthew Wild <mwild1@gmail.com>
parents:
3319
diff
changeset
|
443 local function extract_reference(text, i, j) |
|
8a93af85f319
mod_muc_cloud_notify: Count codepoints instead of bytes
Matthew Wild <mwild1@gmail.com>
parents:
3319
diff
changeset
|
444 -- COMPAT w/ pre-Lua 5.3 |
|
8a93af85f319
mod_muc_cloud_notify: Count codepoints instead of bytes
Matthew Wild <mwild1@gmail.com>
parents:
3319
diff
changeset
|
445 local c, pos, p1 = 0, 0, nil; |
|
8a93af85f319
mod_muc_cloud_notify: Count codepoints instead of bytes
Matthew Wild <mwild1@gmail.com>
parents:
3319
diff
changeset
|
446 for char in text:gmatch("([%z\1-\127\194-\244][\128-\191]*)") do |
|
8a93af85f319
mod_muc_cloud_notify: Count codepoints instead of bytes
Matthew Wild <mwild1@gmail.com>
parents:
3319
diff
changeset
|
447 c, pos = c + 1, pos + #char; |
|
8a93af85f319
mod_muc_cloud_notify: Count codepoints instead of bytes
Matthew Wild <mwild1@gmail.com>
parents:
3319
diff
changeset
|
448 if not p1 and i < c then |
|
8a93af85f319
mod_muc_cloud_notify: Count codepoints instead of bytes
Matthew Wild <mwild1@gmail.com>
parents:
3319
diff
changeset
|
449 p1 = pos; |
|
8a93af85f319
mod_muc_cloud_notify: Count codepoints instead of bytes
Matthew Wild <mwild1@gmail.com>
parents:
3319
diff
changeset
|
450 end |
|
8a93af85f319
mod_muc_cloud_notify: Count codepoints instead of bytes
Matthew Wild <mwild1@gmail.com>
parents:
3319
diff
changeset
|
451 if c == j then |
|
8a93af85f319
mod_muc_cloud_notify: Count codepoints instead of bytes
Matthew Wild <mwild1@gmail.com>
parents:
3319
diff
changeset
|
452 return text:sub(p1, pos); |
|
8a93af85f319
mod_muc_cloud_notify: Count codepoints instead of bytes
Matthew Wild <mwild1@gmail.com>
parents:
3319
diff
changeset
|
453 end |
|
8a93af85f319
mod_muc_cloud_notify: Count codepoints instead of bytes
Matthew Wild <mwild1@gmail.com>
parents:
3319
diff
changeset
|
454 end |
|
8a93af85f319
mod_muc_cloud_notify: Count codepoints instead of bytes
Matthew Wild <mwild1@gmail.com>
parents:
3319
diff
changeset
|
455 end |
| 3319 | 456 |
| 457 -- archive message added | |
| 458 local function archive_message_added(event) | |
| 459 -- event is: { origin = origin, stanza = stanza, for_user = store_user, id = id } | |
| 460 -- only notify for new mam messages when at least one device is online | |
| 461 local room = event.room; | |
| 462 local stanza = event.stanza; | |
| 463 local body = stanza:get_child_text('body'); | |
| 464 | |
| 465 for reference in stanza:childtags("reference", "urn:xmpp:reference:0") do | |
| 466 if reference.attr['type'] == 'mention' and reference.attr['begin'] and reference.attr['end'] then | |
|
3570
8a93af85f319
mod_muc_cloud_notify: Count codepoints instead of bytes
Matthew Wild <mwild1@gmail.com>
parents:
3319
diff
changeset
|
467 local nick = extract_reference(body, reference.attr['begin'], reference.attr['end']); |
| 3319 | 468 local jid = room:get_registered_jid(nick); |
| 469 | |
| 470 if room._occupants[room.jid..'/'..nick] then | |
| 471 -- We only notify for members not currently in the room | |
| 472 module:log("debug", "Not notifying %s, because he's currently in the room", jid); | |
| 473 else | |
| 474 -- We only need to notify once, even when there are multiple mentions. | |
| 475 local user_push_services = push_store:get(jid); | |
| 476 handle_notify_request(event.stanza, jid, user_push_services, true); | |
| 477 return | |
| 478 end | |
| 479 end | |
| 480 end | |
| 481 end | |
| 482 | |
| 483 module:hook("muc-add-history", archive_message_added); | |
| 484 | |
| 485 local function send_ping(event) | |
| 486 local user = event.user; | |
| 487 local user_push_services = push_store:get(user); | |
| 488 local push_services = event.push_services or user_push_services; | |
| 489 handle_notify_request(nil, user, push_services, true); | |
| 490 end | |
| 491 -- can be used by other modules to ping one or more (or all) push endpoints | |
| 492 module:hook("cloud-notify-ping", send_ping); | |
| 493 | |
| 494 module:log("info", "Module loaded"); | |
| 495 function module.unload() | |
| 496 if module.unhook then | |
| 497 module:unhook("account-disco-info", account_dico_info); | |
| 498 module:unhook("iq-set/host/"..xmlns_push..":enable", push_enable); | |
| 499 module:unhook("iq-set/host/"..xmlns_push..":disable", push_disable); | |
| 500 | |
| 501 module:unhook("muc-add-history", archive_message_added); | |
| 502 module:unhook("cloud-notify-ping", send_ping); | |
| 503 | |
| 504 for push_identifier, _ in pairs(push_errors) do | |
| 505 local stanza_id = hashes.sha256(push_identifier, true); | |
| 506 module:unhook("iq-error/host/"..stanza_id, handle_push_error); | |
| 507 module:unhook("iq-result/host/"..stanza_id, handle_push_success); | |
| 508 id2node[stanza_id] = nil; | |
| 509 end | |
| 510 end | |
| 511 | |
| 512 module:log("info", "Module unloaded"); | |
| 513 end |
