Software / code / prosody-modules
Comparison
mod_cloud_notify/mod_cloud_notify.lua @ 3108:cfcb020bcd1d
mod_cloud_notify: inform mod_smacks of first push in hibernated state
| author | tmolitor <thilo@eightysoft.de> |
|---|---|
| date | Fri, 08 Jun 2018 17:39:07 +0200 |
| parent | 3085:1ea6861b533f |
| child | 3619:74aa35aeb08a |
comparison
equal
deleted
inserted
replaced
| 3107:f703cc6e72df | 3108:cfcb020bcd1d |
|---|---|
| 125 services[push_identifier] = data; | 125 services[push_identifier] = data; |
| 126 return self:set(user, services); | 126 return self:set(user, services); |
| 127 end | 127 end |
| 128 return api; | 128 return api; |
| 129 end)(); | 129 end)(); |
| 130 | |
| 130 | 131 |
| 131 -- Forward declarations, as both functions need to reference each other | 132 -- Forward declarations, as both functions need to reference each other |
| 132 local handle_push_success, handle_push_error; | 133 local handle_push_success, handle_push_error; |
| 133 | 134 |
| 134 function handle_push_error(event) | 135 function handle_push_error(event) |
| 154 if host_sessions[node] then | 155 if host_sessions[node] then |
| 155 for _, session in pairs(host_sessions[node].sessions) do | 156 for _, session in pairs(host_sessions[node].sessions) do |
| 156 if session.push_identifier == push_identifier then | 157 if session.push_identifier == push_identifier then |
| 157 session.push_identifier = nil; | 158 session.push_identifier = nil; |
| 158 session.push_settings = nil; | 159 session.push_settings = nil; |
| 160 session.first_hibernated_push = nil; | |
| 159 end | 161 end |
| 160 end | 162 end |
| 161 end | 163 end |
| 162 -- save changed global config | 164 -- save changed global config |
| 163 changed = true; | 165 changed = true; |
| 239 if not ok then | 241 if not ok then |
| 240 origin.send(st.error_reply(stanza, "wait", "internal-server-error")); | 242 origin.send(st.error_reply(stanza, "wait", "internal-server-error")); |
| 241 else | 243 else |
| 242 origin.push_identifier = push_identifier; | 244 origin.push_identifier = push_identifier; |
| 243 origin.push_settings = push_service; | 245 origin.push_settings = push_service; |
| 246 origin.first_hibernated_push = nil; | |
| 244 origin.log("info", "Push notifications enabled for %s (%s)", tostring(stanza.attr.from), tostring(origin.push_identifier)); | 247 origin.log("info", "Push notifications enabled for %s (%s)", tostring(stanza.attr.from), tostring(origin.push_identifier)); |
| 245 origin.send(st.reply(stanza)); | 248 origin.send(st.reply(stanza)); |
| 246 end | 249 end |
| 247 return true; | 250 return true; |
| 248 end | 251 end |
| 262 if push_info.jid == push_jid and (not push_node or push_info.node == push_node) then | 265 if push_info.jid == push_jid and (not push_node or push_info.node == push_node) then |
| 263 origin.log("info", "Push notifications disabled (%s)", tostring(key)); | 266 origin.log("info", "Push notifications disabled (%s)", tostring(key)); |
| 264 if origin.push_identifier == key then | 267 if origin.push_identifier == key then |
| 265 origin.push_identifier = nil; | 268 origin.push_identifier = nil; |
| 266 origin.push_settings = nil; | 269 origin.push_settings = nil; |
| 270 origin.first_hibernated_push = nil; | |
| 267 end | 271 end |
| 268 user_push_services[key] = nil; | 272 user_push_services[key] = nil; |
| 269 push_errors[key] = nil; | 273 push_errors[key] = nil; |
| 270 if module.unhook then | 274 if module.unhook then |
| 271 module:unhook("iq-error/host/"..key, handle_push_error); | 275 module:unhook("iq-error/host/"..key, handle_push_error); |
| 362 { name = "last-message-sender"; type = "jid-single"; }; | 366 { name = "last-message-sender"; type = "jid-single"; }; |
| 363 { name = "last-message-body"; type = "text-single"; }; | 367 { name = "last-message-body"; type = "text-single"; }; |
| 364 }; | 368 }; |
| 365 | 369 |
| 366 -- http://xmpp.org/extensions/xep-0357.html#publishing | 370 -- http://xmpp.org/extensions/xep-0357.html#publishing |
| 367 local function handle_notify_request(stanza, node, user_push_services) | 371 local function handle_notify_request(stanza, node, user_push_services, log_push_decline) |
| 368 local pushes = 0; | 372 local pushes = 0; |
| 369 if not user_push_services or next(user_push_services) == nil then return pushes end | 373 if not user_push_services or next(user_push_services) == nil then return pushes end |
| 370 | 374 |
| 371 for push_identifier, push_info in pairs(user_push_services) do | 375 for push_identifier, push_info in pairs(user_push_services) do |
| 372 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 | 376 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 |
| 373 if stanza then | 377 if stanza then |
| 374 if not stanza._push_notify then stanza._push_notify = {}; end | 378 if not stanza._push_notify then stanza._push_notify = {}; end |
| 375 if stanza._push_notify[push_identifier] then | 379 if stanza._push_notify[push_identifier] then |
| 376 module:log("debug", "Already sent push notification for %s@%s to %s (%s)", node, module.host, push_info.jid, tostring(push_info.node)); | 380 if log_push_decline then |
| 381 module:log("debug", "Already sent push notification for %s@%s to %s (%s)", node, module.host, push_info.jid, tostring(push_info.node)); | |
| 382 end | |
| 377 send_push = false; | 383 send_push = false; |
| 378 end | 384 end |
| 379 stanza._push_notify[push_identifier] = true; | 385 stanza._push_notify[push_identifier] = true; |
| 380 end | 386 end |
| 381 | 387 |
| 394 if stanza and include_sender then | 400 if stanza and include_sender then |
| 395 form_data["last-message-sender"] = stanza.attr.from; | 401 form_data["last-message-sender"] = stanza.attr.from; |
| 396 end | 402 end |
| 397 if stanza and include_body then | 403 if stanza and include_body then |
| 398 form_data["last-message-body"] = stanza:get_child_text("body"); | 404 form_data["last-message-body"] = stanza:get_child_text("body"); |
| 399 elseif stanza and dummy_body ~= "" and is_important(stanza) then | 405 elseif stanza and dummy_body and is_important(stanza) then |
| 400 form_data["last-message-body"] = dummy_body; | 406 form_data["last-message-body"] = tostring(dummy_body); |
| 401 end | 407 end |
| 402 push_publish:add_child(push_form:form(form_data)); | 408 push_publish:add_child(push_form:form(form_data)); |
| 403 push_publish:up(); -- / notification | 409 push_publish:up(); -- / notification |
| 404 push_publish:up(); -- / publish | 410 push_publish:up(); -- / publish |
| 405 push_publish:up(); -- / pubsub | 411 push_publish:up(); -- / pubsub |
| 406 if push_info.options then | 412 if push_info.options then |
| 407 push_publish:tag("publish-options"):add_child(st.deserialize(push_info.options)); | 413 push_publish:tag("publish-options"):add_child(st.deserialize(push_info.options)); |
| 408 end | 414 end |
| 409 -- send out push | 415 -- send out push |
| 410 module:log("debug", "Sending push notification for %s@%s to %s (%s)", node, module.host, push_info.jid, tostring(push_info.node)); | 416 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)); |
| 411 -- module:log("debug", "PUSH STANZA: %s", tostring(push_publish)); | 417 -- module:log("debug", "PUSH STANZA: %s", tostring(push_publish)); |
| 412 -- handle push errors for this node | 418 -- handle push errors for this node |
| 413 if push_errors[push_identifier] == nil then | 419 if push_errors[push_identifier] == nil then |
| 414 push_errors[push_identifier] = 0; | 420 push_errors[push_identifier] = 0; |
| 415 module:hook("iq-error/host/"..stanza_id, handle_push_error); | 421 module:hook("iq-error/host/"..stanza_id, handle_push_error); |
| 433 | 439 |
| 434 -- publish on offline message | 440 -- publish on offline message |
| 435 module:hook("message/offline/handle", function(event) | 441 module:hook("message/offline/handle", function(event) |
| 436 local node, user_push_services = get_push_settings(event.stanza, event.origin); | 442 local node, user_push_services = get_push_settings(event.stanza, event.origin); |
| 437 module:log("debug", "Invoking cloud handle_notify_request() for offline stanza"); | 443 module:log("debug", "Invoking cloud handle_notify_request() for offline stanza"); |
| 438 handle_notify_request(event.stanza, node, user_push_services); | 444 handle_notify_request(event.stanza, node, user_push_services, true); |
| 439 end, 1); | 445 end, 1); |
| 440 | 446 |
| 441 -- publish on unacked smacks message | 447 -- publish on unacked smacks message |
| 442 local function process_smacks_stanza(stanza, session) | 448 local function process_smacks_stanza(stanza, session) |
| 443 if session.push_identifier then | 449 if session.push_identifier then |
| 444 session.log("debug", "Invoking cloud handle_notify_request() for smacks queued stanza"); | 450 session.log("debug", "Invoking cloud handle_notify_request() for smacks queued stanza"); |
| 445 local user_push_services = {[session.push_identifier] = session.push_settings}; | 451 local user_push_services = {[session.push_identifier] = session.push_settings}; |
| 446 local node = get_push_settings(stanza, session); | 452 local node = get_push_settings(stanza, session); |
| 447 handle_notify_request(stanza, node, user_push_services); | 453 if handle_notify_request(stanza, node, user_push_services, true) ~= 0 then |
| 454 if session.hibernating and not session.first_hibernated_push then | |
| 455 -- if important stanzas are treated differently (pushed with last-message-body field set to dummy string) | |
| 456 -- and the message was important (e.g. had a last-message-body field) OR if we treat all pushes equally, | |
| 457 -- then record the time of first push in the session for the smack module which will extend its hibernation | |
| 458 -- timeout based on the value of session.first_hibernated_push | |
| 459 if not dummy_body or (dummy_body and is_important(stanza)) then | |
| 460 session.first_hibernated_push = os_time(); | |
| 461 end | |
| 462 end | |
| 463 end | |
| 448 end | 464 end |
| 449 return stanza; | 465 return stanza; |
| 450 end | 466 end |
| 451 | 467 |
| 452 local function process_smacks_queue(queue, session) | 468 local function process_smacks_queue(queue, session) |
| 453 if not session.push_identifier then return; end | 469 if not session.push_identifier then return; end |
| 454 local user_push_services = {[session.push_identifier] = session.push_settings}; | 470 local user_push_services = {[session.push_identifier] = session.push_settings}; |
| 471 local notified = { unimportant = false; important = false } | |
| 455 for i=1, #queue do | 472 for i=1, #queue do |
| 456 local stanza = queue[i]; | 473 local stanza = queue[i]; |
| 457 local node = get_push_settings(stanza, session); | 474 local node = get_push_settings(stanza, session); |
| 458 session.log("debug", "Invoking cloud handle_notify_request() for smacks queued stanza: %d", i); | 475 stanza_type = "unimportant" |
| 459 if handle_notify_request(stanza, node, user_push_services) ~= 0 then | 476 if dummy_body and is_important(stanza) then stanza_type = "important"; end |
| 460 session.log("debug", "Cloud handle_notify_request() > 0, not notifying for other queued stanzas"); | 477 if not notified[stanza_type] then -- only notify if we didn't try to push for this stanza type already |
| 461 return; -- only notify for one stanza in the queue, not for all in a row | 478 -- session.log("debug", "Invoking cloud handle_notify_request() for smacks queued stanza: %d", i); |
| 479 if handle_notify_request(stanza, node, user_push_services, false) ~= 0 then | |
| 480 if session.hibernating and not session.first_hibernated_push then | |
| 481 -- if important stanzas are treated differently (pushed with last-message-body field set to dummy string) | |
| 482 -- and the message was important (e.g. had a last-message-body field) OR if we treat all pushes equally, | |
| 483 -- then record the time of first push in the session for the smack module which will extend its hibernation | |
| 484 -- timeout based on the value of session.first_hibernated_push | |
| 485 if not dummy_body or (dummy_body and is_important(stanza)) then | |
| 486 session.first_hibernated_push = os_time(); | |
| 487 end | |
| 488 end | |
| 489 session.log("debug", "Cloud handle_notify_request() > 0, not notifying for other queued stanzas of type %s", stanza_type); | |
| 490 notified[stanza_type] = true | |
| 491 end | |
| 462 end | 492 end |
| 463 end | 493 end |
| 464 end | 494 end |
| 465 | 495 |
| 466 -- smacks hibernation is started | 496 -- smacks hibernation is started |
| 467 local function hibernate_session(event) | 497 local function hibernate_session(event) |
| 468 local session = event.origin; | 498 local session = event.origin; |
| 469 local queue = event.queue; | 499 local queue = event.queue; |
| 500 session.first_hibernated_push = nil; | |
| 470 -- process unacked stanzas | 501 -- process unacked stanzas |
| 471 process_smacks_queue(queue, session); | 502 process_smacks_queue(queue, session); |
| 472 -- process future unacked (hibernated) stanzas | 503 -- process future unacked (hibernated) stanzas |
| 473 filters.add_filter(session, "stanzas/out", process_smacks_stanza, -990); | 504 filters.add_filter(session, "stanzas/out", process_smacks_stanza, -990); |
| 474 end | 505 end |
| 476 -- smacks hibernation is ended | 507 -- smacks hibernation is ended |
| 477 local function restore_session(event) | 508 local function restore_session(event) |
| 478 local session = event.resumed; | 509 local session = event.resumed; |
| 479 if session then -- older smacks module versions send only the "intermediate" session in event.session and no session.resumed one | 510 if session then -- older smacks module versions send only the "intermediate" session in event.session and no session.resumed one |
| 480 filters.remove_filter(session, "stanzas/out", process_smacks_stanza); | 511 filters.remove_filter(session, "stanzas/out", process_smacks_stanza); |
| 512 session.first_hibernated_push = nil; | |
| 481 end | 513 end |
| 482 end | 514 end |
| 483 | 515 |
| 484 -- smacks ack is delayed | 516 -- smacks ack is delayed |
| 485 local function ack_delayed(event) | 517 local function ack_delayed(event) |
| 503 if event.for_user == to then | 535 if event.for_user == to then |
| 504 local user_push_services = push_store:get(to); | 536 local user_push_services = push_store:get(to); |
| 505 if next(user_push_services) == nil then return end | 537 if next(user_push_services) == nil then return end |
| 506 | 538 |
| 507 -- only notify nodes with no active sessions (smacks is counted as active and handled separate) | 539 -- only notify nodes with no active sessions (smacks is counted as active and handled separate) |
| 508 local notify_push_sevices = {}; | 540 local notify_push_services = {}; |
| 509 for identifier, push_info in pairs(user_push_services) do | 541 for identifier, push_info in pairs(user_push_services) do |
| 510 local identifier_found = nil; | 542 local identifier_found = nil; |
| 511 for _, session in pairs(user_session) do | 543 for _, session in pairs(user_session) do |
| 512 -- module:log("debug", "searching for '%s': identifier '%s' for session %s", tostring(identifier), tostring(session.push_identifier), tostring(session.full_jid)); | 544 -- module:log("debug", "searching for '%s': identifier '%s' for session %s", tostring(identifier), tostring(session.push_identifier), tostring(session.full_jid)); |
| 513 if session.push_identifier == identifier then | 545 if session.push_identifier == identifier then |
| 516 end | 548 end |
| 517 end | 549 end |
| 518 if identifier_found then | 550 if identifier_found then |
| 519 identifier_found.log("debug", "Not cloud notifying '%s' of new MAM stanza (session still alive)", identifier); | 551 identifier_found.log("debug", "Not cloud notifying '%s' of new MAM stanza (session still alive)", identifier); |
| 520 else | 552 else |
| 521 notify_push_sevices[identifier] = push_info; | 553 notify_push_services[identifier] = push_info; |
| 522 end | 554 end |
| 523 end | 555 end |
| 524 | 556 |
| 525 handle_notify_request(event.stanza, to, notify_push_sevices); | 557 handle_notify_request(event.stanza, to, notify_push_services, true); |
| 526 end | 558 end |
| 527 end | 559 end |
| 528 | 560 |
| 529 module:hook("smacks-hibernation-start", hibernate_session); | 561 module:hook("smacks-hibernation-start", hibernate_session); |
| 530 module:hook("smacks-hibernation-end", restore_session); | 562 module:hook("smacks-hibernation-end", restore_session); |
| 533 | 565 |
| 534 local function send_ping(event) | 566 local function send_ping(event) |
| 535 local user = event.user; | 567 local user = event.user; |
| 536 local user_push_services = push_store:get(user); | 568 local user_push_services = push_store:get(user); |
| 537 local push_services = event.push_services or user_push_services; | 569 local push_services = event.push_services or user_push_services; |
| 538 handle_notify_request(nil, user, push_services); | 570 handle_notify_request(nil, user, push_services, true); |
| 539 end | 571 end |
| 540 -- can be used by other modules to ping one or more (or all) push endpoints | 572 -- can be used by other modules to ping one or more (or all) push endpoints |
| 541 module:hook("cloud-notify-ping", send_ping); | 573 module:hook("cloud-notify-ping", send_ping); |
| 542 | 574 |
| 543 module:log("info", "Module loaded"); | 575 module:log("info", "Module loaded"); |